/***********************************************************************
THIS FILE IS AUTOMATICALLY GENERATED. DO NOT MODIFY
DEVELOPER: Zihan Chen(vczh)
***********************************************************************/
#include "GacUI.h"
#ifndef VCZH_DEBUG_NO_REFLECTION
#include "GacUIReflection.h"
#endif

/***********************************************************************
.\GRAPHICSCOMPOSITION\GUIGRAPHICSEVENTRECEIVER.CPP
***********************************************************************/

namespace vl
{
	namespace presentation
	{
		namespace compositions
		{

/***********************************************************************
Event Receiver
***********************************************************************/

			GuiGraphicsEventReceiver::GuiGraphicsEventReceiver(GuiGraphicsComposition* _sender)
				:sender(_sender)
				,leftButtonDown(_sender)
				,leftButtonUp(_sender)
				,leftButtonDoubleClick(_sender)
				,middleButtonDown(_sender)
				,middleButtonUp(_sender)
				,middleButtonDoubleClick(_sender)
				,rightButtonDown(_sender)
				,rightButtonUp(_sender)
				,rightButtonDoubleClick(_sender)
				,horizontalWheel(_sender)
				,verticalWheel(_sender)
				,mouseMove(_sender)
				,mouseEnter(_sender)
				,mouseLeave(_sender)
				,previewKey(_sender)
				,keyDown(_sender)
				,keyUp(_sender)
				,systemKeyDown(_sender)
				,systemKeyUp(_sender)
				,previewCharInput(_sender)
				,charInput(_sender)
				,gotFocus(_sender)
				,lostFocus(_sender)
				,caretNotify(_sender)
				,clipboardNotify(_sender)
			{
			}

			GuiGraphicsEventReceiver::~GuiGraphicsEventReceiver()
			{
			}

			GuiGraphicsComposition* GuiGraphicsEventReceiver::GetAssociatedComposition()
			{
				return sender;
			}
		}
	}
}

/***********************************************************************
.\GRAPHICSCOMPOSITION\GUIGRAPHICSSPECIALIZEDCOMPOSITION.CPP
***********************************************************************/

namespace vl
{
	namespace presentation
	{
		namespace compositions
		{

/***********************************************************************
GuiSideAlignedComposition
***********************************************************************/

			GuiSideAlignedComposition::GuiSideAlignedComposition()
				:direction(Top)
				,maxLength(10)
				,maxRatio(1.0)
			{
			}

			GuiSideAlignedComposition::~GuiSideAlignedComposition()
			{
			}

			GuiSideAlignedComposition::Direction GuiSideAlignedComposition::GetDirection()
			{
				return direction;
			}

			void GuiSideAlignedComposition::SetDirection(Direction value)
			{
				direction = value;
				InvokeOnCompositionStateChanged();
			}

			vint GuiSideAlignedComposition::GetMaxLength()
			{
				return maxLength;
			}

			void GuiSideAlignedComposition::SetMaxLength(vint value)
			{
				if (value < 0) value = 0;
				maxLength = value;
				InvokeOnCompositionStateChanged();
			}

			double GuiSideAlignedComposition::GetMaxRatio()
			{
				return maxRatio;
			}

			void GuiSideAlignedComposition::SetMaxRatio(double value)
			{
				maxRatio =
					value < 0 ? 0 :
					value>1 ? 1 :
					value;
				InvokeOnCompositionStateChanged();
			}

			bool GuiSideAlignedComposition::IsSizeAffectParent()
			{
				return false;
			}

			Rect GuiSideAlignedComposition::GetBounds()
			{
				Rect result;
				GuiGraphicsComposition* parent = GetParent();
				if (parent)
				{
					Rect bounds = parent->GetBounds();
					vint w = (vint)(bounds.Width()*maxRatio);
					vint h = (vint)(bounds.Height()*maxRatio);
					if (w > maxLength) w = maxLength;
					if (h > maxLength) h = maxLength;
					switch (direction)
					{
					case Left:
						{
							bounds.x2 = bounds.x1 + w;
						}
						break;
					case Top:
						{
							bounds.y2 = bounds.y1 + h;
						}
						break;
					case Right:
						{
							bounds.x1 = bounds.x2 - w;
						}
						break;
					case Bottom:
						{
							bounds.y1 = bounds.y2 - h;
						}
						break;
					}
					result = bounds;
				}
				UpdatePreviousBounds(result);
				return result;
			}

/***********************************************************************
GuiPartialViewComposition
***********************************************************************/

			GuiPartialViewComposition::GuiPartialViewComposition()
				:wRatio(0.0)
				,wPageSize(1.0)
				,hRatio(0.0)
				,hPageSize(1.0)
			{
			}

			GuiPartialViewComposition::~GuiPartialViewComposition()
			{
			}

			double GuiPartialViewComposition::GetWidthRatio()
			{
				return wRatio;
			}

			double GuiPartialViewComposition::GetWidthPageSize()
			{
				return wPageSize;
			}

			double GuiPartialViewComposition::GetHeightRatio()
			{
				return hRatio;
			}

			double GuiPartialViewComposition::GetHeightPageSize()
			{
				return hPageSize;
			}

			void GuiPartialViewComposition::SetWidthRatio(double value)
			{
				wRatio = value;
				InvokeOnCompositionStateChanged();
			}

			void GuiPartialViewComposition::SetWidthPageSize(double value)
			{
				wPageSize = value;
				InvokeOnCompositionStateChanged();
			}

			void GuiPartialViewComposition::SetHeightRatio(double value)
			{
				hRatio = value;
				InvokeOnCompositionStateChanged();
			}

			void GuiPartialViewComposition::SetHeightPageSize(double value)
			{
				hPageSize = value;
				InvokeOnCompositionStateChanged();
			}

			bool GuiPartialViewComposition::IsSizeAffectParent()
			{
				return false;
			}

			Rect GuiPartialViewComposition::GetBounds()
			{
				Rect result;
				GuiGraphicsComposition* parent = GetParent();
				if (parent)
				{
					Rect bounds = parent->GetBounds();
					vint w = bounds.Width();
					vint h = bounds.Height();
					vint pw = (vint)(wPageSize*w);
					vint ph = (vint)(hPageSize*h);

					vint ow = preferredMinSize.x - pw;
					if (ow < 0) ow = 0;
					vint oh = preferredMinSize.y - ph;
					if (oh < 0) oh = 0;

					w -= ow;
					h -= oh;
					pw += ow;
					ph += oh;

					result = Rect(Point((vint)(wRatio*w), (vint)(hRatio*h)), Size(pw, ph));
				}
				UpdatePreviousBounds(result);
				return result;
			}
		}
	}
}


/***********************************************************************
.\GRAPHICSCOMPOSITION\GUIGRAPHICSSTACKCOMPOSITION.CPP
***********************************************************************/

namespace vl
{
	namespace presentation
	{
		namespace compositions
		{

/***********************************************************************
GuiStackComposition
***********************************************************************/

			void GuiStackComposition::UpdateStackItemBounds()
			{
				if (stackItemBounds.Count() != stackItems.Count())
				{
					stackItemBounds.Resize(stackItems.Count());
				}

				stackItemTotalSize = Size(0, 0);
				Point offset;
				for (vint i = 0; i < stackItems.Count(); i++)
				{
					vint offsetX = 0;
					vint offsetY = 0;
					Size itemSize = stackItems[i]->GetMinSize();
					stackItemBounds[i] = Rect(offset, itemSize);

#define ACCUMULATE(U, V)										\
					{											\
						if (stackItemTotalSize.V < itemSize.V)	\
						{										\
							stackItemTotalSize.V = itemSize.V;	\
						}										\
						if (i > 0)								\
						{										\
							stackItemTotalSize.U += padding;	\
						}										\
						stackItemTotalSize.U += itemSize.U;		\
					}											\

					switch (direction)
					{
					case GuiStackComposition::Horizontal:
					case GuiStackComposition::ReversedHorizontal:
						ACCUMULATE(x, y)
						break;
					case GuiStackComposition::Vertical:
					case GuiStackComposition::ReversedVertical:
						ACCUMULATE(y, x)
						break;
					}

#undef ACCUMULATE
					offset.x += itemSize.x + padding;
					offset.y += itemSize.y + padding;
				}
				EnsureStackItemVisible();
			}

			void GuiStackComposition::EnsureStackItemVisible()
			{
#define ADJUSTMENT(U, V)															\
				if (itemBounds.U() <= 0)											\
				{																	\
					adjustment -= itemBounds.U();									\
				}																	\
				else																\
				{																	\
					vint overflow = itemBounds.V() - previousBounds.V();			\
					if (overflow > 0)												\
					{																\
						adjustment -= overflow;										\
					}																\
				}																	\

				if (ensuringVisibleStackItem)
				{
					Rect itemBounds = ensuringVisibleStackItem->GetBounds();
					switch (direction)
					{
					case Horizontal:
					case ReversedHorizontal:
						ADJUSTMENT(Left, Right)
						break;
					case Vertical:
					case ReversedVertical:
						ADJUSTMENT(Top, Bottom)
						break;
					}
				}

				InvokeOnCompositionStateChanged();
#undef ADJUSTMENT
			}

			void GuiStackComposition::OnBoundsChanged(GuiGraphicsComposition* sender, GuiEventArgs& arguments)
			{
				EnsureStackItemVisible();
			}

			void GuiStackComposition::OnChildInserted(GuiGraphicsComposition* child)
			{
				GuiBoundsComposition::OnChildInserted(child);
				GuiStackItemComposition* item = dynamic_cast<GuiStackItemComposition*>(child);
				if (item)
				{
					if (!stackItems.Contains(item))
					{
						stackItems.Add(item);
					}
					UpdateStackItemBounds();
				}
			}

			void GuiStackComposition::OnChildRemoved(GuiGraphicsComposition* child)
			{
				GuiBoundsComposition::OnChildRemoved(child);
				GuiStackItemComposition* item = dynamic_cast<GuiStackItemComposition*>(child);
				if (item)
				{
					stackItems.Remove(item);
					if (item == ensuringVisibleStackItem)
					{
						ensuringVisibleStackItem = 0;
					}
					UpdateStackItemBounds();
				}
			}

			GuiStackComposition::GuiStackComposition()
			{
				BoundsChanged.AttachMethod(this, &GuiStackComposition::OnBoundsChanged);
			}

			GuiStackComposition::~GuiStackComposition()
			{
			}

			const GuiStackComposition::ItemCompositionList& GuiStackComposition::GetStackItems()
			{
				return stackItems;
			}

			bool GuiStackComposition::InsertStackItem(vint index, GuiStackItemComposition* item)
			{
				index = stackItems.Insert(index, item);
				if (!AddChild(item))
				{
					stackItems.RemoveAt(index);
					return false;
				}
				else
				{
					return true;
				}
			}

			GuiStackComposition::Direction GuiStackComposition::GetDirection()
			{
				return direction;
			}

			void GuiStackComposition::SetDirection(Direction value)
			{
				direction = value;
				EnsureStackItemVisible();
			}

			vint GuiStackComposition::GetPadding()
			{
				return padding;
			}

			void GuiStackComposition::SetPadding(vint value)
			{
				padding = value;
				EnsureStackItemVisible();
			}

			void GuiStackComposition::ForceCalculateSizeImmediately()
			{
				GuiBoundsComposition::ForceCalculateSizeImmediately();
				UpdateStackItemBounds();
			}
			
			Size GuiStackComposition::GetMinPreferredClientSize()
			{
				Size minSize = GuiBoundsComposition::GetMinPreferredClientSize();
				if (GetMinSizeLimitation() == GuiGraphicsComposition::LimitToElementAndChildren)
				{
					if (!ensuringVisibleStackItem || direction == Vertical || direction == ReversedVertical)
					{
						if (minSize.x < stackItemTotalSize.x)
						{
							minSize.x = stackItemTotalSize.x;
						}
					}
					if (!ensuringVisibleStackItem || direction == Horizontal || direction == ReversedHorizontal)
					{
						if (minSize.y < stackItemTotalSize.y)
						{
							minSize.y = stackItemTotalSize.y;
						}
					}
				}

				vint x = 0;
				vint y = 0;
				if (extraMargin.left > 0) x += extraMargin.left;
				if (extraMargin.right > 0) x += extraMargin.right;
				if (extraMargin.top > 0) y += extraMargin.top;
				if (extraMargin.bottom > 0) y += extraMargin.bottom;
				return minSize + Size(x, y);
			}

			Rect GuiStackComposition::GetBounds()
			{
				for (vint i = 0; i < stackItems.Count(); i++)
				{
					if (stackItemBounds[i].GetSize() != stackItems[i]->GetMinSize())
					{
						UpdateStackItemBounds();
						break;
					}
				}

				Rect bounds = GuiBoundsComposition::GetBounds();
				previousBounds = bounds;
				UpdatePreviousBounds(previousBounds);
				return bounds;
			}

			Margin GuiStackComposition::GetExtraMargin()
			{
				return extraMargin;
			}

			void GuiStackComposition::SetExtraMargin(Margin value)
			{
				extraMargin=value;
				EnsureStackItemVisible();
			}

			bool GuiStackComposition::IsStackItemClipped()
			{
				Rect clientArea = GetClientArea();
				switch(direction)
				{
				case Horizontal:
				case ReversedHorizontal:
					{
						vint width = stackItemTotalSize.x
							+ (extraMargin.left > 0 ? extraMargin.left : 0)
							+ (extraMargin.right > 0 ? extraMargin.right : 0)
							;
						return width > clientArea.Width();
					}
					break;
				case Vertical:
				case ReversedVertical:
					{
						vint height = stackItemTotalSize.y
							+ (extraMargin.top > 0 ? extraMargin.top : 0)
							+ (extraMargin.bottom > 0 ? extraMargin.bottom : 0)
							;
						return height > clientArea.Height();
					}
					break;
				}
				return false;
			}

			bool GuiStackComposition::EnsureVisible(vint index)
			{
				if (0 <= index && index < stackItems.Count())
				{
					ensuringVisibleStackItem = stackItems[index];
				}
				else
				{
					ensuringVisibleStackItem = 0;
				}
				EnsureStackItemVisible();
				return ensuringVisibleStackItem != 0;
			}

/***********************************************************************
GuiStackItemComposition
***********************************************************************/

			void GuiStackItemComposition::OnParentChanged(GuiGraphicsComposition* oldParent, GuiGraphicsComposition* newParent)
			{
				GuiGraphicsSite::OnParentChanged(oldParent, newParent);
				stackParent = newParent == 0 ? 0 : dynamic_cast<GuiStackComposition*>(newParent);
			}

			Size GuiStackItemComposition::GetMinSize()
			{
				return GetBoundsInternal(bounds).GetSize();
			}

			GuiStackItemComposition::GuiStackItemComposition()
				:stackParent(0)
			{
				SetMinSizeLimitation(GuiGraphicsComposition::LimitToElementAndChildren);
			}

			GuiStackItemComposition::~GuiStackItemComposition()
			{
			}

			bool GuiStackItemComposition::IsSizeAffectParent()
			{
				return false;
			}

			Rect GuiStackItemComposition::GetBounds()
			{
				Rect result = bounds;
				if(stackParent)
				{
					vint index = stackParent->stackItems.IndexOf(this);
					if (index != -1)
					{
						result = stackParent->stackItemBounds[index];
					}

					Rect parentBounds = stackParent->previousBounds;
					Margin margin = stackParent->extraMargin;
					if (margin.left <= 0) margin.left = 0;
					if (margin.top <= 0) margin.top = 0;
					if (margin.right <= 0) margin.right = 0;
					if (margin.bottom <= 0) margin.bottom = 0;

					auto x = result.Left();
					auto y = result.Top();
					auto w = result.Width();
					auto h = result.Height();

					switch (stackParent->direction)
					{
					case GuiStackComposition::Horizontal:
						x += margin.left + stackParent->adjustment;
						y = margin.top;
						h = parentBounds.Height() - margin.top - margin.bottom;
						break;
					case GuiStackComposition::ReversedHorizontal:
						x = parentBounds.Width() - margin.right - x - w + stackParent->adjustment;
						y = margin.top;
						h = parentBounds.Height() - margin.top - margin.bottom;
						break;
					case GuiStackComposition::Vertical:
						x = margin.left;
						y += margin.top + stackParent->adjustment;
						w = parentBounds.Width() - margin.left - margin.right;
						break;
					case GuiStackComposition::ReversedVertical:
						x = margin.left;
						y = parentBounds.Height() - margin.bottom - y - h + stackParent->adjustment;
						w = parentBounds.Width() - margin.left - margin.right;
						break;
					}

					result = Rect(
						x - extraMargin.left,
						y - extraMargin.top,
						x + w + extraMargin.right,
						y + h + extraMargin.bottom
						);
				}
				UpdatePreviousBounds(result);
				return result;
			}

			void GuiStackItemComposition::SetBounds(Rect value)
			{
				bounds = value;
				InvokeOnCompositionStateChanged();
			}

			Margin GuiStackItemComposition::GetExtraMargin()
			{
				return extraMargin;
			}

			void GuiStackItemComposition::SetExtraMargin(Margin value)
			{
				extraMargin = value;
				InvokeOnCompositionStateChanged();
			}
		}
	}
}


/***********************************************************************
.\GRAPHICSCOMPOSITION\GUIGRAPHICSTABLECOMPOSITION.CPP
***********************************************************************/
#include <math.h>

namespace vl
{
	namespace presentation
	{
		namespace compositions
		{
			using namespace collections;
			using namespace controls;
			using namespace elements;

/***********************************************************************
GuiTableComposition
***********************************************************************/

			namespace update_cell_bounds_helpers
			{
				vint First(vint a, vint b)
				{
					return a;
				}

				vint Second(vint a, vint b)
				{
					return b;
				}

				vint X(Size s)
				{
					return s.x;
				}

				vint Y(Size s)
				{
					return s.y;
				}

				vint RL(GuiCellComposition* cell)
				{
					return cell->GetRow();
				}

				vint CL(GuiCellComposition* cell)
				{
					return cell->GetColumn();
				}

				vint RS(GuiCellComposition* cell)
				{
					return cell->GetRowSpan();
				}

				vint CS(GuiCellComposition* cell)
				{
					return cell->GetColumnSpan();
				}
			}
			using namespace update_cell_bounds_helpers;

			vint GuiTableComposition::GetSiteIndex(vint _rows, vint _columns, vint _row, vint _column)
			{
				return _row*_columns + _column;
			}

			void GuiTableComposition::SetSitedCell(vint _row, vint _column, GuiCellComposition* cell)
			{
				cellCompositions[GetSiteIndex(rows, columns, _row, _column)] = cell;
			}

			void GuiTableComposition::UpdateCellBoundsInternal(
				collections::Array<vint>& dimSizes,
				vint& dimSize,
				vint& dimSizeWithPercentage,
				collections::Array<GuiCellOption>& dimOptions,
				vint GuiTableComposition::* dim1,
				vint GuiTableComposition::* dim2,
				vint(*getSize)(Size),
				vint(*getLocation)(GuiCellComposition*),
				vint(*getSpan)(GuiCellComposition*),
				vint(*getRow)(vint, vint),
				vint(*getCol)(vint, vint),
				vint maxPass
			)
			{
				for (vint pass = 0; pass < maxPass; pass++)
				{
					for (vint i = 0; i < this->*dim1; i++)
					{
						GuiCellOption option = dimOptions[i];
						if (pass == 0)
						{
							dimSizes[i] = 0;
						}
						switch (option.composeType)
						{
						case GuiCellOption::Absolute:
							{
								dimSizes[i] = option.absolute;
							}
							break;
						case GuiCellOption::MinSize:
							{
								for (vint j = 0; j < this->*dim2; j++)
								{
									GuiCellComposition* cell = GetSitedCell(getRow(i, j), getCol(i, j));
									if (cell)
									{
										bool accept = false;
										if (pass == 0)
										{
											accept = getSpan(cell) == 1;
										}
										else
										{
											accept = getLocation(cell) + getSpan(cell) == i + 1;
										}
										if (accept)
										{
											vint size = getSize(cell->GetPreferredBounds().GetSize());
											vint span = getSpan(cell);
											for (vint k = 1; k < span; k++)
											{
												size -= dimSizes[i - k] + cellPadding;
											}
											if (dimSizes[i] < size)
											{
												dimSizes[i] = size;
											}
										}
									}
								}
							}
							break;
						default:;
						}
					}
				}

				bool percentageExists = false;
				for (vint i = 0; i < this->*dim1; i++)
				{
					GuiCellOption option = dimOptions[i];
					if (option.composeType == GuiCellOption::Percentage)
					{
						if (0.001 < option.percentage)
						{
							percentageExists = true;
						}
					}
				}

				if (percentageExists)
				{
					for (vint i = 0; i < this->*dim1; i++)
					{
						GuiCellOption option = dimOptions[i];
						if (option.composeType == GuiCellOption::Percentage)
						{
							if (0.001 < option.percentage)
							{
								for (vint j = 0; j < this->*dim2; j++)
								{
									GuiCellComposition* cell = GetSitedCell(getRow(i, j), getCol(i, j));
									if (cell)
									{
										vint size = getSize(cell->GetPreferredBounds().GetSize());
										vint start = getLocation(cell);
										vint span = getSpan(cell);
										size -= (span - 1)*cellPadding;
										double totalPercentage = 0;

										for (vint k = start; k < start + span; k++)
										{
											if (dimOptions[k].composeType == GuiCellOption::Percentage)
											{
												if (0.001 < dimOptions[k].percentage)
												{
													totalPercentage += dimOptions[k].percentage;
												}
											}
											else
											{
												size -= dimSizes[k];
											}
										}

										size = (vint)ceil(size*option.percentage / totalPercentage);
										if (dimSizes[i] < size)
										{
											dimSizes[i] = size;
										}
									}
								}
							}
						}
					}

					vint percentageTotalSize = 0;
					for (vint i = 0; i < this->*dim1; i++)
					{
						GuiCellOption option = dimOptions[i];
						if (option.composeType == GuiCellOption::Percentage)
						{
							if (0.001 < option.percentage)
							{
								vint size = (vint)ceil(dimSizes[i] / option.percentage);
								if (percentageTotalSize < size)
								{
									percentageTotalSize = size;
								}
							}
						}
					}

					double totalPercentage = 0;
					for (vint i = 0; i < this->*dim1; i++)
					{
						GuiCellOption option = dimOptions[i];
						if (option.composeType == GuiCellOption::Percentage)
						{
							if (0.001 < option.percentage)
							{
								totalPercentage += option.percentage;
							}
						}
					}

					for (vint i = 0; i < this->*dim1; i++)
					{
						GuiCellOption option = dimOptions[i];
						if (option.composeType == GuiCellOption::Percentage)
						{
							if (0.001 < option.percentage)
							{
								vint size = (vint)ceil(percentageTotalSize*option.percentage / totalPercentage);
								if (dimSizes[i] < size)
								{
									dimSizes[i] = size;
								}
							}
						}
					}
				}

				for (vint i = 0; i < this->*dim1; i++)
				{
					if (dimOptions[i].composeType != GuiCellOption::Percentage)
					{
						dimSize += dimSizes[i];
					}
					dimSizeWithPercentage += dimSizes[i];
				}
			}

			void GuiTableComposition::UpdateCellBoundsPercentages(
				collections::Array<vint>& dimSizes,
				vint dimSize,
				vint maxDimSize,
				collections::Array<GuiCellOption>& dimOptions
			)
			{
				if (maxDimSize > dimSize)
				{
					double totalPercentage = 0;
					vint percentageCount = 0;
					for (vint i = 0; i < dimOptions.Count(); i++)
					{
						GuiCellOption option = dimOptions[i];
						if (option.composeType == GuiCellOption::Percentage)
						{
							totalPercentage += option.percentage;
							percentageCount++;
						}
					}
					if (percentageCount > 0 && totalPercentage > 0.001)
					{
						for (vint i = 0; i < dimOptions.Count(); i++)
						{
							GuiCellOption option = dimOptions[i];
							if (option.composeType == GuiCellOption::Percentage)
							{
								dimSizes[i] = (vint)((maxDimSize - dimSize)*option.percentage / totalPercentage);
							}
						}
					}
				}
			}

			vint GuiTableComposition::UpdateCellBoundsOffsets(
				collections::Array<vint>& offsets,
				collections::Array<vint>& sizes,
				vint max
				)
			{
				offsets[0] = 0;
				for (vint i = 1; i < offsets.Count(); i++)
				{
					offsets[i] = offsets[i - 1] + cellPadding + sizes[i - 1];
				}

				vint last = offsets.Count() - 1;
				vint right = offsets[last] + sizes[last];
				return max - right;
			}

			void GuiTableComposition::UpdateCellBoundsInternal()
			{
				rowOffsets.Resize(rows);
				rowSizes.Resize(rows);
				columnOffsets.Resize(columns);
				columnSizes.Resize(columns);
				{
					vint rowTotal = (rows - 1)*cellPadding;
					vint columnTotal = (columns - 1)*cellPadding;
					vint rowTotalWithPercentage = rowTotal;
					vint columnTotalWithPercentage = columnTotal;

					UpdateCellBoundsInternal(
						rowSizes,
						rowTotal,
						rowTotalWithPercentage,
						rowOptions,
						&GuiTableComposition::rows,
						&GuiTableComposition::columns,
						&Y,
						&RL,
						&RS,
						&First,
						&Second,
						1
					);
					UpdateCellBoundsInternal(
						columnSizes,
						columnTotal,
						columnTotalWithPercentage,
						columnOptions,
						&GuiTableComposition::columns,
						&GuiTableComposition::rows,
						&X,
						&CL,
						&CS,
						&Second,
						&First,
						1
					);

					Rect area = GetCellArea();
					UpdateCellBoundsPercentages(rowSizes, rowTotal, area.Height(), rowOptions);
					UpdateCellBoundsPercentages(columnSizes, columnTotal, area.Width(), columnOptions);
					rowExtending = UpdateCellBoundsOffsets(rowOffsets, rowSizes, area.Height());
					columnExtending = UpdateCellBoundsOffsets(columnOffsets, columnSizes, area.Width());

					for (vint i = 0; i < rows; i++)
					{
						for (vint j = 0; j < columns; j++)
						{
							vint index = GetSiteIndex(rows, columns, i, j);
							cellBounds[index] = Rect(Point(columnOffsets[j], rowOffsets[i]), Size(columnSizes[j], rowSizes[i]));
						}
					}
				}
			}

			void GuiTableComposition::UpdateTableContentMinSize()
			{
				Array<vint> rowSizes, columnSizes;
				rowSizes.Resize(rows);
				columnSizes.Resize(columns);
				{
					vint rowTotal = (rows - 1) * cellPadding;
					vint columnTotal = (columns - 1) * cellPadding;
					vint rowTotalWithPercentage = rowTotal;
					vint columnTotalWithPercentage = columnTotal;

					UpdateCellBoundsInternal(
						rowSizes,
						rowTotal,
						rowTotalWithPercentage,
						rowOptions,
						&GuiTableComposition::rows,
						&GuiTableComposition::columns,
						&Y,
						&RL,
						&RS,
						&First,
						&Second,
						2
						);
					UpdateCellBoundsInternal(
						columnSizes,
						columnTotal,
						columnTotalWithPercentage,
						columnOptions,
						&GuiTableComposition::columns,
						&GuiTableComposition::rows,
						&X,
						&CL,
						&CS,
						&Second,
						&First,
						2
						);
					tableContentMinSize = Size(columnTotalWithPercentage, rowTotalWithPercentage);
				}
				if (previousContentMinSize != tableContentMinSize)
				{
					previousContentMinSize = tableContentMinSize;
					UpdateCellBoundsInternal();
					InvokeOnCompositionStateChanged();
				}
			}

			void GuiTableComposition::OnRenderContextChanged()
			{
				if(GetRenderTarget())
				{
					UpdateTableContentMinSize();
				}
			}

			GuiTableComposition::GuiTableComposition()
				:rows(0)
				, columns(0)
				, cellPadding(0)
				, borderVisible(true)
				, rowExtending(0)
				, columnExtending(0)
			{
				ConfigChanged.SetAssociatedComposition(this);
				SetRowsAndColumns(1, 1);
			}

			GuiTableComposition::~GuiTableComposition()
			{
			}

			vint GuiTableComposition::GetRows()
			{
				return rows;
			}

			vint GuiTableComposition::GetColumns()
			{
				return columns;
			}

			bool GuiTableComposition::SetRowsAndColumns(vint _rows, vint _columns)
			{
				if (_rows <= 0 || _columns <= 0) return false;
				rowOptions.Resize(_rows);
				columnOptions.Resize(_columns);
				cellCompositions.Resize(_rows*_columns);
				cellBounds.Resize(_rows*_columns);
				for (vint i = 0; i < _rows*_columns; i++)
				{
					cellCompositions[i] = 0;
					cellBounds[i] = Rect();
				}
				rows = _rows;
				columns = _columns;
				vint childCount = Children().Count();
				for (vint i = 0; i < childCount; i++)
				{
					GuiCellComposition* cell = dynamic_cast<GuiCellComposition*>(Children().Get(i));
					if (cell)
					{
						cell->OnTableRowsAndColumnsChanged();
					}
				}
				ConfigChanged.Execute(GuiEventArgs(this));
				UpdateCellBounds();
				InvokeOnCompositionStateChanged();
				return true;
			}

			GuiCellComposition* GuiTableComposition::GetSitedCell(vint _row, vint _column)
			{
				return cellCompositions[GetSiteIndex(rows, columns, _row, _column)];
			}

			GuiCellOption GuiTableComposition::GetRowOption(vint _row)
			{
				return rowOptions[_row];
			}

			void GuiTableComposition::SetRowOption(vint _row, GuiCellOption option)
			{
				rowOptions[_row] = option;
				UpdateCellBounds();
				InvokeOnCompositionStateChanged();
				ConfigChanged.Execute(GuiEventArgs(this));
			}

			GuiCellOption GuiTableComposition::GetColumnOption(vint _column)
			{
				return columnOptions[_column];
			}

			void GuiTableComposition::SetColumnOption(vint _column, GuiCellOption option)
			{
				columnOptions[_column] = option;
				UpdateCellBounds();
				InvokeOnCompositionStateChanged();
				ConfigChanged.Execute(GuiEventArgs(this));
			}

			vint GuiTableComposition::GetCellPadding()
			{
				return cellPadding;
			}

			void GuiTableComposition::SetCellPadding(vint value)
			{
				if (value < 0) value = 0;
				cellPadding = value;
				UpdateCellBounds();
				InvokeOnCompositionStateChanged();
			}

			bool GuiTableComposition::GetBorderVisible()
			{
				return borderVisible;
			}

			void GuiTableComposition::SetBorderVisible(bool value)
			{
				if (borderVisible != value)
				{
					borderVisible = value;
					UpdateCellBounds();
					InvokeOnCompositionStateChanged();
				}
			}

			Rect GuiTableComposition::GetCellArea()
			{
				Rect bounds(Point(0, 0), GuiBoundsComposition::GetBounds().GetSize());
				vint borderThickness = borderVisible ? cellPadding : 0;
				bounds.x1 += margin.left + internalMargin.left + borderThickness;
				bounds.y1 += margin.top + internalMargin.top + borderThickness;
				bounds.x2 -= margin.right + internalMargin.right + borderThickness;
				bounds.y2 -= margin.bottom + internalMargin.bottom + borderThickness;
				if (bounds.x2 < bounds.x1) bounds.x2 = bounds.x1;
				if (bounds.y2 < bounds.y1) bounds.y2 = bounds.y1;
				return bounds;
			}

			void GuiTableComposition::UpdateCellBounds()
			{
				UpdateCellBoundsInternal();
				UpdateTableContentMinSize();
			}

			void GuiTableComposition::ForceCalculateSizeImmediately()
			{
				GuiBoundsComposition::ForceCalculateSizeImmediately();
				UpdateCellBounds();
				UpdateCellBounds();
			}

			Size GuiTableComposition::GetMinPreferredClientSize()
			{
				vint offset = (borderVisible ? 2 * cellPadding : 0);
				return Size(tableContentMinSize.x + offset, tableContentMinSize.y + offset);
			}

			Rect GuiTableComposition::GetBounds()
			{
				Rect cached = previousBounds;
				Rect result = GuiBoundsComposition::GetBounds();

				bool cellMinSizeModified = false;
				SortedList<GuiCellComposition*> cells;
				FOREACH(GuiCellComposition*, cell, cellCompositions)
				{
					if (cell && !cells.Contains(cell))
					{
						cells.Add(cell);
						Size newSize = cell->GetPreferredBounds().GetSize();
						if (cell->lastPreferredSize != newSize)
						{
							cell->lastPreferredSize = newSize;
							cellMinSizeModified = true;
						}
					}
				}

				if (cached != result || cellMinSizeModified)
				{
					UpdateCellBounds();
					InvokeOnCompositionStateChanged();
				}
				return result;
			}

/***********************************************************************
GuiCellComposition
***********************************************************************/

			void GuiCellComposition::ClearSitedCells(GuiTableComposition* table)
			{
				if (row != -1 && column != -1)
				{
					for (vint r = 0; r < rowSpan; r++)
					{
						for (vint c = 0; c < columnSpan; c++)
						{
							table->SetSitedCell(row + r, column + c, 0);
						}
					}
				}
			}

			void GuiCellComposition::SetSitedCells(GuiTableComposition* table)
			{
				for (vint r = 0; r < rowSpan; r++)
				{
					for (vint c = 0; c < columnSpan; c++)
					{
						table->SetSitedCell(row + r, column + c, this);
					}
				}
			}

			void GuiCellComposition::ResetSiteInternal()
			{
				row = -1;
				column = -1;
				rowSpan = 1;
				columnSpan = 1;
			}

			bool GuiCellComposition::SetSiteInternal(vint _row, vint _column, vint _rowSpan, vint _columnSpan)
			{
				if (tableParent)
				{
					if (_row < 0 || _row >= tableParent->rows || _column < 0 || _column >= tableParent->columns) return false;
					if (_rowSpan<1 || _row + _rowSpan>tableParent->rows || _columnSpan<1 || _column + _columnSpan>tableParent->columns) return false;

					for (vint r = 0; r < _rowSpan; r++)
					{
						for (vint c = 0; c < _columnSpan; c++)
						{
							GuiCellComposition* cell = tableParent->GetSitedCell(_row + r, _column + c);
							if (cell && cell != this)
							{
								return false;
							}
						}
					}
					ClearSitedCells(tableParent);
				}

				row = _row;
				column = _column;
				rowSpan = _rowSpan;
				columnSpan = _columnSpan;

				if (tableParent)
				{
					SetSitedCells(tableParent);
				}
				return true;
			}

			void GuiCellComposition::OnParentChanged(GuiGraphicsComposition* oldParent, GuiGraphicsComposition* newParent)
			{
				GuiGraphicsSite::OnParentChanged(oldParent, newParent);
				if (tableParent)
				{
					ClearSitedCells(tableParent);
				}
				tableParent = dynamic_cast<GuiTableComposition*>(newParent);
				if (!tableParent || !SetSiteInternal(row, column, rowSpan, columnSpan))
				{
					ResetSiteInternal();
				}
				if (tableParent)
				{
					if (row != -1 && column != -1)
					{
						SetSiteInternal(row, column, rowSpan, columnSpan);
					}
					tableParent->UpdateCellBounds();
				}
			}

			void GuiCellComposition::OnTableRowsAndColumnsChanged()
			{
				if(!SetSiteInternal(row, column, rowSpan, columnSpan))
				{
					ResetSiteInternal();
				}
			}

			GuiCellComposition::GuiCellComposition()
				:row(-1)
				,column(-1)
				,rowSpan(1)
				,columnSpan(1)
				,tableParent(0)
			{
				SetMinSizeLimitation(GuiGraphicsComposition::LimitToElementAndChildren);
			}

			GuiCellComposition::~GuiCellComposition()
			{
			}

			GuiTableComposition* GuiCellComposition::GetTableParent()
			{
				return tableParent;
			}

			vint GuiCellComposition::GetRow()
			{
				return row;
			}

			vint GuiCellComposition::GetRowSpan()
			{
				return rowSpan;
			}

			vint GuiCellComposition::GetColumn()
			{
				return column;
			}

			vint GuiCellComposition::GetColumnSpan()
			{
				return columnSpan;
			}

			bool GuiCellComposition::SetSite(vint _row, vint _column, vint _rowSpan, vint _columnSpan)
			{
				if(SetSiteInternal(_row, _column, _rowSpan, _columnSpan))
				{
					if (tableParent)
					{
						tableParent->UpdateCellBounds();
					}
					InvokeOnCompositionStateChanged();
					return true;
				}
				else
				{
					return false;
				}
			}

			Rect GuiCellComposition::GetBounds()
			{
				Rect result;
				if(tableParent && row!=-1 && column!=-1)
				{
					Rect bounds1, bounds2;
					{
						vint index=tableParent->GetSiteIndex(tableParent->rows, tableParent->columns, row, column);
						bounds1=tableParent->cellBounds[index];
					}
					{
						vint index=tableParent->GetSiteIndex(tableParent->rows, tableParent->columns, row+rowSpan-1, column+columnSpan-1);
						bounds2=tableParent->cellBounds[index];
						if(tableParent->GetMinSizeLimitation()==GuiGraphicsComposition::NoLimit)
						{
							if(row+rowSpan==tableParent->rows)
							{
								bounds2.y2+=tableParent->rowExtending;
							}
							if(column+columnSpan==tableParent->columns)
							{
								bounds2.x2+=tableParent->columnExtending;
							}
						}
					}
					vint offset = tableParent->borderVisible ? tableParent->cellPadding : 0;
					result = Rect(bounds1.x1 + offset, bounds1.y1 + offset, bounds2.x2 + offset, bounds2.y2 + offset);
				}
				else
				{
					result = Rect();
				}
				UpdatePreviousBounds(result);
				return result;
			}

/***********************************************************************
GuiTableSplitterCompositionBase
***********************************************************************/

			void GuiTableSplitterCompositionBase::OnParentChanged(GuiGraphicsComposition* oldParent, GuiGraphicsComposition* newParent)
			{
				GuiGraphicsSite::OnParentChanged(oldParent, newParent);
				tableParent = dynamic_cast<GuiTableComposition*>(newParent);
			}

			void GuiTableSplitterCompositionBase::OnLeftButtonDown(GuiGraphicsComposition* sender, GuiMouseEventArgs& arguments)
			{
				dragging = true;
				draggingPoint = Point(arguments.x, arguments.y);
			}

			void GuiTableSplitterCompositionBase::OnLeftButtonUp(GuiGraphicsComposition* sender, GuiMouseEventArgs& arguments)
			{
				dragging = false;
			}

			void GuiTableSplitterCompositionBase::OnMouseMoveHelper(
				vint cellsBefore,
				vint GuiTableComposition::* cells,
				collections::Array<vint>& cellSizes,
				vint offset,
				GuiCellOption(GuiTableComposition::*getOption)(vint),
				void(GuiTableComposition::*setOption)(vint, GuiCellOption)
				)
			{
				if (dragging)
				{
					if (tableParent)
					{
						if (0 < cellsBefore && cellsBefore < tableParent->*cells)
						{
							auto o1 = (tableParent->*getOption)(cellsBefore - 1);
							auto o2 = (tableParent->*getOption)(cellsBefore);

							vint indexStart = -1;
							vint indexEnd = -1;
							vint indexStep = -1;
							vint max = 0;

							if (offset < 0)
							{
								indexStart = cellsBefore - 1;
								indexEnd = -1;
								indexStep = -1;
							}
							else if (offset > 0)
							{
								indexStart = cellsBefore;
								indexEnd = tableParent->*cells;
								indexStep = 1;
							}
							else
							{
								return;
							}

							{
								auto o = (tableParent->*getOption)(indexStart);
								if (o.composeType == GuiCellOption::Absolute)
								{
									max = o.absolute - 1;
								}
								else
								{
									for (vint i = indexStart; i != indexEnd; i += indexStep)
									{
										o = (tableParent->*getOption)(i);
										if (o.composeType == GuiCellOption::Absolute)
										{
											break;
										}
										else if (o.composeType == GuiCellOption::Percentage)
										{
											max += cellSizes[i] - 1;
										}
									}
								}

								if (max <= 0)
								{
									return;
								}
							}

							if (offset < 0)
							{
								if (max < -offset)
								{
									offset = -max;
								}
							}
							else
							{
								if (max < offset)
								{
									offset = max;
								}
							}

							if (o1.composeType == GuiCellOption::Absolute)
							{
								o1.absolute += offset;
								(tableParent->*setOption)(cellsBefore - 1, o1);
							}
							if (o2.composeType == GuiCellOption::Absolute)
							{
								o2.absolute -= offset;
								(tableParent->*setOption)(cellsBefore, o2);
							}
							tableParent->ForceCalculateSizeImmediately();
						}
					}
				}
			}

			Rect GuiTableSplitterCompositionBase::GetBoundsHelper(
				vint cellsBefore,
				vint GuiTableComposition::* cells,
				vint(Rect::* dimSize)()const,
				collections::Array<vint>& cellOffsets,
				vint Rect::* dimU1,
				vint Rect::* dimU2,
				vint Rect::* dimV1,
				vint Rect::* dimV2
				)
			{
				Rect result(0, 0, 0, 0);
				if (tableParent)
				{
					if (0 < cellsBefore && cellsBefore < tableParent->*cells)
					{
						vint offset = tableParent->borderVisible ? tableParent->cellPadding : 0;
						result.*dimU1 = offset;
						result.*dimU2 = offset + (tableParent->GetCellArea().*dimSize)();
						result.*dimV1 = offset + cellOffsets[cellsBefore] - tableParent->cellPadding;
						result.*dimV2 = (result.*dimV1) + tableParent->cellPadding;
					}
				}
				UpdatePreviousBounds(result);
				return result;
			}
			
			GuiTableSplitterCompositionBase::GuiTableSplitterCompositionBase()
				:tableParent(0)
				, dragging(false)
			{
				SetAssociatedCursor(GetCurrentController()->ResourceService()->GetSystemCursor(INativeCursor::SizeNS));
				GetEventReceiver()->leftButtonDown.AttachMethod(this, &GuiTableSplitterCompositionBase::OnLeftButtonDown);
				GetEventReceiver()->leftButtonUp.AttachMethod(this, &GuiTableSplitterCompositionBase::OnLeftButtonUp);
			}

			GuiTableSplitterCompositionBase::~GuiTableSplitterCompositionBase()
			{
			}

			GuiTableComposition* GuiTableSplitterCompositionBase::GetTableParent()
			{
				return tableParent;
			}

/***********************************************************************
GuiRowSplitterComposition
***********************************************************************/

			void GuiRowSplitterComposition::OnMouseMove(GuiGraphicsComposition* sender, GuiMouseEventArgs& arguments)
			{
				OnMouseMoveHelper(
					rowsToTheTop,
					&GuiTableComposition::rows,
					tableParent->rowSizes,
					arguments.y - draggingPoint.y,
					&GuiTableComposition::GetRowOption,
					&GuiTableComposition::SetRowOption
					);
			}
			
			GuiRowSplitterComposition::GuiRowSplitterComposition()
				:rowsToTheTop(0)
			{
				SetAssociatedCursor(GetCurrentController()->ResourceService()->GetSystemCursor(INativeCursor::SizeNS));
				GetEventReceiver()->mouseMove.AttachMethod(this, &GuiRowSplitterComposition::OnMouseMove);
			}

			GuiRowSplitterComposition::~GuiRowSplitterComposition()
			{
			}

			vint GuiRowSplitterComposition::GetRowsToTheTop()
			{
				return rowsToTheTop;
			}

			void GuiRowSplitterComposition::SetRowsToTheTop(vint value)
			{
				rowsToTheTop = value;
				InvokeOnCompositionStateChanged();
			}

			Rect GuiRowSplitterComposition::GetBounds()
			{
				return GetBoundsHelper(
					rowsToTheTop,
					&GuiTableComposition::rows,
					&Rect::Width,
					tableParent->rowOffsets,
					&Rect::x1,
					&Rect::x2,
					&Rect::y1,
					&Rect::y2
					);
			}

/***********************************************************************
GuiColumnSplitterComposition
***********************************************************************/

			void GuiColumnSplitterComposition::OnMouseMove(GuiGraphicsComposition* sender, GuiMouseEventArgs& arguments)
			{
				OnMouseMoveHelper(
					columnsToTheLeft,
					&GuiTableComposition::columns,
					tableParent->columnSizes,
					arguments.x - draggingPoint.x,
					&GuiTableComposition::GetColumnOption,
					&GuiTableComposition::SetColumnOption
					);
			}
			
			GuiColumnSplitterComposition::GuiColumnSplitterComposition()
				:columnsToTheLeft(0)
			{
				SetAssociatedCursor(GetCurrentController()->ResourceService()->GetSystemCursor(INativeCursor::SizeWE));
				GetEventReceiver()->mouseMove.AttachMethod(this, &GuiColumnSplitterComposition::OnMouseMove);
			}

			GuiColumnSplitterComposition::~GuiColumnSplitterComposition()
			{
			}

			vint GuiColumnSplitterComposition::GetColumnsToTheLeft()
			{
				return columnsToTheLeft;
			}

			void GuiColumnSplitterComposition::SetColumnsToTheLeft(vint value)
			{
				columnsToTheLeft = value;
				InvokeOnCompositionStateChanged();
			}

			Rect GuiColumnSplitterComposition::GetBounds()
			{
				return GetBoundsHelper(
					columnsToTheLeft,
					&GuiTableComposition::columns,
					&Rect::Height,
					tableParent->columnOffsets,
					&Rect::y1,
					&Rect::y2,
					&Rect::x1,
					&Rect::x2
					);
			}
		}
	}
}

/***********************************************************************
.\GRAPHICSELEMENT\GUIGRAPHICSDOCUMENTELEMENT.CPP
***********************************************************************/

namespace vl
{
	using namespace collections;

	namespace presentation
	{
		namespace elements
		{

/***********************************************************************
SetPropertiesVisitor
***********************************************************************/

			namespace visitors
			{
				class SetPropertiesVisitor : public Object, public DocumentRun::IVisitor
				{
					typedef GuiDocumentElement::GuiDocumentElementRenderer	Renderer;
					typedef DocumentModel::ResolvedStyle					ResolvedStyle;
				public:
					vint							start;
					vint							length;
					vint							selectionBegin;
					vint							selectionEnd;
					List<ResolvedStyle>				styles;

					DocumentModel*					model;
					Renderer*						renderer;
					Ptr<Renderer::ParagraphCache>	cache;
					IGuiGraphicsParagraph*			paragraph;

					SetPropertiesVisitor(DocumentModel* _model, Renderer* _renderer, Ptr<Renderer::ParagraphCache> _cache, vint _selectionBegin, vint _selectionEnd)
						:start(0)
						,length(0)
						,model(_model)
						,renderer(_renderer)
						,cache(_cache)
						,paragraph(_cache->graphicsParagraph.Obj())
						,selectionBegin(_selectionBegin)
						,selectionEnd(_selectionEnd)
					{
						ResolvedStyle style;
						style=model->GetStyle(DocumentModel::DefaultStyleName, style);
						styles.Add(style);
					}

					void VisitContainer(DocumentContainerRun* run)
					{
						FOREACH(Ptr<DocumentRun>, subRun, run->runs)
						{
							subRun->Accept(this);
						}
					}

					void ApplyStyle(vint start, vint length, const ResolvedStyle& style)
					{
						paragraph->SetFont(start, length, style.style.fontFamily);
						paragraph->SetSize(start, length, style.style.size);
						paragraph->SetStyle(start, length, 
							(IGuiGraphicsParagraph::TextStyle)
							( (style.style.bold?IGuiGraphicsParagraph::Bold:0)
							| (style.style.italic?IGuiGraphicsParagraph::Italic:0)
							| (style.style.underline?IGuiGraphicsParagraph::Underline:0)
							| (style.style.strikeline?IGuiGraphicsParagraph::Strikeline:0)
							));
					}

					void ApplyColor(vint start, vint length, const ResolvedStyle& style)
					{
						paragraph->SetColor(start, length, style.color);
						paragraph->SetBackgroundColor(start, length, style.backgroundColor);
					}

					void Visit(DocumentTextRun* run)override
					{
						length=run->GetRepresentationText().Length();
						if(length>0)
						{
							ResolvedStyle style=styles[styles.Count()-1];
							ApplyStyle(start, length, style);
							ApplyColor(start, length, style);

							vint styleStart=start;
							vint styleEnd=styleStart+length;
							if(styleStart<selectionEnd && selectionBegin<styleEnd)
							{
								vint s2=styleStart>selectionBegin?styleStart:selectionBegin;
								vint s3=selectionEnd<styleEnd?selectionEnd:styleEnd;

								if(s2<s3)
								{
									ResolvedStyle selectionStyle=model->GetStyle(DocumentModel::SelectionStyleName, style);
									ApplyColor(s2, s3-s2, selectionStyle);
								}
							}
						}
						start+=length;
					}

					void Visit(DocumentStylePropertiesRun* run)override
					{
						ResolvedStyle style=styles[styles.Count()-1];
						style=model->GetStyle(run->style, style);
						styles.Add(style);
						VisitContainer(run);
						styles.RemoveAt(styles.Count()-1);
					}

					void Visit(DocumentStyleApplicationRun* run)override
					{
						ResolvedStyle style=styles[styles.Count()-1];
						style=model->GetStyle(run->styleName, style);
						styles.Add(style);
						VisitContainer(run);
						styles.RemoveAt(styles.Count()-1);
					}

					void Visit(DocumentHyperlinkRun* run)override
					{
						ResolvedStyle style=styles[styles.Count()-1];
						style=model->GetStyle(run->styleName, style);
						styles.Add(style);
						VisitContainer(run);
						styles.RemoveAt(styles.Count()-1);
					}

					void Visit(DocumentImageRun* run)override
					{
						length=run->GetRepresentationText().Length();

						Ptr<GuiImageFrameElement> element=GuiImageFrameElement::Create();
						element->SetImage(run->image, run->frameIndex);
						element->SetStretch(true);

						IGuiGraphicsParagraph::InlineObjectProperties properties;
						properties.size=run->size;
						properties.baseline=run->baseline;
						properties.breakCondition=IGuiGraphicsParagraph::Alone;
						properties.backgroundImage = element;

						paragraph->SetInlineObject(start, length, properties);

						if(start<selectionEnd && selectionBegin<start+length)
						{
							ResolvedStyle style=styles[styles.Count()-1];
							ResolvedStyle selectionStyle=model->GetStyle(DocumentModel::SelectionStyleName, style);
							ApplyColor(start, length, selectionStyle);
						}
						start+=length;
					}

					void Visit(DocumentEmbeddedObjectRun* run)override
					{
						length=run->GetRepresentationText().Length();

						IGuiGraphicsParagraph::InlineObjectProperties properties;
						properties.breakCondition=IGuiGraphicsParagraph::Alone;

						if (run->name != L"")
						{
							vint index = renderer->nameCallbackIdMap.Keys().IndexOf(run->name);
							if (index != -1)
							{
								auto id = renderer->nameCallbackIdMap.Values()[index];
								index = cache->embeddedObjects.Keys().IndexOf(id);
								if (index != -1)
								{
									auto eo = cache->embeddedObjects.Values()[index];
									if (eo->start == start)
									{
										properties.size = eo->size;
										properties.callbackId = id;
									}
								}
							}
							else
							{
								auto eo = MakePtr<Renderer::EmbeddedObject>();
								eo->name = run->name;
								eo->size = Size(0, 0);
								eo->start = start;

								vint id = -1;
								vint count = renderer->freeCallbackIds.Count();
								if (count > 0)
								{
									id = renderer->freeCallbackIds[count - 1];
									renderer->freeCallbackIds.RemoveAt(count - 1);
								}
								else
								{
									id = renderer->usedCallbackIds++;
								}

								renderer->nameCallbackIdMap.Add(eo->name, id);
								cache->embeddedObjects.Add(id, eo);
								properties.callbackId = id;
							}
						}

						paragraph->SetInlineObject(start, length, properties);

						if(start<selectionEnd && selectionBegin<start+length)
						{
							ResolvedStyle style=styles[styles.Count()-1];
							ResolvedStyle selectionStyle=model->GetStyle(DocumentModel::SelectionStyleName, style);
							ApplyColor(start, length, selectionStyle);
						}
						start+=length;
					}

					void Visit(DocumentParagraphRun* run)override
					{
						VisitContainer(run);
					}

					static vint SetProperty(DocumentModel* model, Renderer* renderer, Ptr<Renderer::ParagraphCache> cache, Ptr<DocumentParagraphRun> run, vint selectionBegin, vint selectionEnd)
					{
						SetPropertiesVisitor visitor(model, renderer, cache, selectionBegin, selectionEnd);
						run->Accept(&visitor);
						return visitor.length;
					}
				};
			}
			using namespace visitors;

/***********************************************************************
GuiDocumentElement::GuiDocumentElementRenderer
***********************************************************************/

			Size GuiDocumentElement::GuiDocumentElementRenderer::OnRenderInlineObject(vint callbackId, Rect location)
			{
				if (element->callback)
				{
					auto cache = paragraphCaches[renderingParagraph];
					auto relativeLocation = Rect(Point(location.x1 + renderingParagraphOffset.x, location.y1 + renderingParagraphOffset.y), location.GetSize());
					auto eo = cache->embeddedObjects[callbackId];
					auto size = element->callback->OnRenderEmbeddedObject(eo->name, relativeLocation);
					eo->resized = eo->size != size;
					eo->size = size;
					return eo->size;
				}
				else
				{
					return Size();
				}
			}

			void GuiDocumentElement::GuiDocumentElementRenderer::InitializeInternal()
			{
			}

			void GuiDocumentElement::GuiDocumentElementRenderer::FinalizeInternal()
			{
			}

			void GuiDocumentElement::GuiDocumentElementRenderer::RenderTargetChangedInternal(IGuiGraphicsRenderTarget* oldRenderTarget, IGuiGraphicsRenderTarget* newRenderTarget)
			{
				for(vint i=0;i<paragraphCaches.Count();i++)
				{
					ParagraphCache* cache=paragraphCaches[i].Obj();
					if(cache)
					{
						cache->graphicsParagraph=0;
					}
				}
			}

			Ptr<GuiDocumentElement::GuiDocumentElementRenderer::ParagraphCache> GuiDocumentElement::GuiDocumentElementRenderer::EnsureAndGetCache(vint paragraphIndex, bool createParagraph)
			{
				if(paragraphIndex<0 || paragraphIndex>=paragraphCaches.Count()) return 0;
				Ptr<DocumentParagraphRun> paragraph=element->document->paragraphs[paragraphIndex];
				Ptr<ParagraphCache> cache=paragraphCaches[paragraphIndex];
				if(!cache)
				{
					cache=new ParagraphCache;
					cache->fullText=paragraph->GetText(false);
					paragraphCaches[paragraphIndex]=cache;
				}

				if(createParagraph)
				{
					if(!cache->graphicsParagraph)
					{
						cache->graphicsParagraph=layoutProvider->CreateParagraph(cache->fullText, renderTarget, this);
						cache->graphicsParagraph->SetParagraphAlignment(paragraph->alignment ? paragraph->alignment.Value() : Alignment::Left);
						SetPropertiesVisitor::SetProperty(element->document.Obj(), this, cache, paragraph, cache->selectionBegin, cache->selectionEnd);
					}
					if(cache->graphicsParagraph->GetMaxWidth()!=lastMaxWidth)
					{
						cache->graphicsParagraph->SetMaxWidth(lastMaxWidth);
					}

					vint paragraphHeight=paragraphHeights[paragraphIndex];
					vint height=cache->graphicsParagraph->GetHeight();
					if(paragraphHeight!=height)
					{
						cachedTotalHeight+=height-paragraphHeight;
						paragraphHeight=height;
						paragraphHeights[paragraphIndex]=paragraphHeight;
						minSize=Size(0, cachedTotalHeight);
					}
				}

				return cache;
			}

			bool GuiDocumentElement::GuiDocumentElementRenderer::GetParagraphIndexFromPoint(Point point, vint& top, vint& index)
			{
				vint y=0;
				for(vint i=0;i<paragraphHeights.Count();i++)
				{
					vint paragraphHeight=paragraphHeights[i];
					vint nextY=y+paragraphHeight+paragraphDistance;
					top=y;
					index=i;

					if(nextY<=point.y)
					{
						y=nextY;
						continue;
					}
					else
					{
						break;
					}
				}
				return true;
			}

			GuiDocumentElement::GuiDocumentElementRenderer::GuiDocumentElementRenderer()
				:paragraphDistance(0)
				,lastMaxWidth(-1)
				,cachedTotalHeight(0)
				,layoutProvider(GetGuiGraphicsResourceManager()->GetLayoutProvider())
				,lastCaret(-1, -1)
				,lastCaretFrontSide(false)
			{
			}

			void GuiDocumentElement::GuiDocumentElementRenderer::Render(Rect bounds)
			{
				if (element->callback)
				{
					element->callback->OnStartRender();
				}
				renderTarget->PushClipper(bounds);
				if(!renderTarget->IsClipperCoverWholeTarget())
				{
					vint maxWidth=bounds.Width();
					Rect clipper=renderTarget->GetClipper();
					vint cx=bounds.Left();
					vint cy=bounds.Top();
					vint y1=clipper.Top()-bounds.Top();
					vint y2=y1+clipper.Height();
					vint y=0;

					lastMaxWidth=maxWidth;

					for(vint i=0;i<paragraphHeights.Count();i++)
					{
						vint paragraphHeight=paragraphHeights[i];
						if(y+paragraphHeight<=y1)
						{
							y+=paragraphHeight+paragraphDistance;
							continue;
						}
						else if(y>=y2)
						{
							break;
						}
						else
						{
							Ptr<DocumentParagraphRun> paragraph=element->document->paragraphs[i];
							Ptr<ParagraphCache> cache=paragraphCaches[i];
							bool created=cache && cache->graphicsParagraph;
							cache=EnsureAndGetCache(i, true);
							if(!created && i==lastCaret.row && element->caretVisible)
							{
								cache->graphicsParagraph->OpenCaret(lastCaret.column, lastCaretColor, lastCaretFrontSide);
							}

							paragraphHeight=cache->graphicsParagraph->GetHeight();

							renderingParagraph = i;
							renderingParagraphOffset = Point(cx - bounds.x1, cy + y - bounds.y1);
							cache->graphicsParagraph->Render(Rect(Point(cx, cy+y), Size(maxWidth, paragraphHeight)));
							renderingParagraph = -1;

							bool resized = false;
							for (vint j = 0; j < cache->embeddedObjects.Count(); j++)
							{
								auto eo = cache->embeddedObjects.Values()[j];
								if (eo->resized)
								{
									eo->resized = false;
									resized = true;
								}
							}

							if (resized)
							{
								cache->graphicsParagraph = 0;
							}
						}

						y+=paragraphHeight+paragraphDistance;
					}
				}
				renderTarget->PopClipper();
				if (element->callback)
				{
					element->callback->OnFinishRender();
				}
			}

			void GuiDocumentElement::GuiDocumentElementRenderer::OnElementStateChanged()
			{
				if (element->document && element->document->paragraphs.Count() > 0)
				{
					vint defaultSize = GetCurrentController()->ResourceService()->GetDefaultFont().size;
					paragraphDistance = defaultSize;
					vint defaultHeight = defaultSize;

					paragraphCaches.Resize(element->document->paragraphs.Count());
					paragraphHeights.Resize(element->document->paragraphs.Count());
					
					for (vint i = 0; i < paragraphCaches.Count(); i++)
					{
						paragraphCaches[i] = 0;
					}
					for (vint i = 0; i < paragraphHeights.Count(); i++)
					{
						paragraphHeights[i] = defaultHeight;
					}

					cachedTotalHeight = paragraphHeights.Count() * (defaultHeight + paragraphDistance);
					if (paragraphHeights.Count()>0)
					{
						cachedTotalHeight -= paragraphDistance;
					}
					minSize = Size(0, cachedTotalHeight);
				}
				else
				{
					paragraphCaches.Resize(0);
					paragraphHeights.Resize(0);
					cachedTotalHeight = 0;
					minSize = Size(0, 0);
				}

				nameCallbackIdMap.Clear();
				freeCallbackIds.Clear();
				usedCallbackIds = 0;
			}

			void GuiDocumentElement::GuiDocumentElementRenderer::NotifyParagraphUpdated(vint index, vint oldCount, vint newCount, bool updatedText)
			{
				if (0 <= index && index < paragraphCaches.Count() && 0 <= oldCount && index + oldCount <= paragraphCaches.Count() && 0 <= newCount)
				{
					vint paragraphCount = element->document->paragraphs.Count();
					CHECK_ERROR(updatedText || oldCount == newCount, L"GuiDocumentlement::GuiDocumentElementRenderer::NotifyParagraphUpdated(vint, vint, vint, bool)#Illegal values of oldCount and newCount.");
					CHECK_ERROR(paragraphCount - paragraphCaches.Count() == newCount - oldCount, L"GuiDocumentElement::GuiDocumentElementRenderer::NotifyParagraphUpdated(vint, vint, vint, bool)#Illegal values of oldCount and newCount.");

					ParagraphCacheArray oldCaches;
					CopyFrom(oldCaches, paragraphCaches);
					paragraphCaches.Resize(paragraphCount);

					ParagraphHeightArray oldHeights;
					CopyFrom(oldHeights, paragraphHeights);
					paragraphHeights.Resize(paragraphCount);

					vint defaultHeight = GetCurrentController()->ResourceService()->GetDefaultFont().size;
					cachedTotalHeight = 0;

					for (vint i = 0; i < paragraphCount; i++)
					{
						if (i < index)
						{
							paragraphCaches[i] = oldCaches[i];
							paragraphHeights[i] = oldHeights[i];
						}
						else if (i < index + newCount)
						{
							paragraphCaches[i] = 0;
							paragraphHeights[i] = defaultHeight;
							if (!updatedText && i < index + oldCount)
							{
								auto cache = oldCaches[i];
								if(cache)
								{
									cache->graphicsParagraph = 0;
								}
								paragraphCaches[i] = cache;
								paragraphHeights[i] = oldHeights[i];
							}
						}
						else
						{
							paragraphCaches[i] = oldCaches[i - (newCount - oldCount)];
							paragraphHeights[i] = oldHeights[i - (newCount - oldCount)];
						}
						cachedTotalHeight += paragraphHeights[i] + paragraphDistance;
					}
					if (paragraphCount > 0)
					{
						cachedTotalHeight -= paragraphDistance;
					}

					if (updatedText)
					{
						vint count = oldCount < newCount ? oldCount : newCount;
						for (vint i = 0; i < count; i++)
						{
							if (auto cache = oldCaches[index + i])
							{
								for (vint j = 0; j < cache->embeddedObjects.Count(); j++)
								{
									auto id = cache->embeddedObjects.Keys()[j];
									auto name = cache->embeddedObjects.Values()[j]->name;
									nameCallbackIdMap.Remove(name);
									freeCallbackIds.Add(id);
								}
							}
						}
					}
				}
			}

			Ptr<DocumentHyperlinkRun::Package> GuiDocumentElement::GuiDocumentElementRenderer::GetHyperlinkFromPoint(Point point)
			{
				vint top=0;
				vint index=-1;
				if(GetParagraphIndexFromPoint(point, top, index))
				{
					Ptr<ParagraphCache> cache=EnsureAndGetCache(index, true);
					Point paragraphPoint(point.x, point.y-top);

					vint start=-1;
					vint length=0;
					if(cache->graphicsParagraph->GetInlineObjectFromPoint(paragraphPoint, start, length))
					{
						return element->document->GetHyperlink(index, start, start+length);
					}

					vint caret=cache->graphicsParagraph->GetCaretFromPoint(paragraphPoint);
					return element->document->GetHyperlink(index, caret, caret);
				}
				return 0;
			}

			void GuiDocumentElement::GuiDocumentElementRenderer::OpenCaret(TextPos caret, Color color, bool frontSide)
			{
				CloseCaret(caret);
				lastCaret=caret;
				lastCaretColor=color;
				lastCaretFrontSide=frontSide;

				Ptr<ParagraphCache> cache=paragraphCaches[lastCaret.row];
				if(cache && cache->graphicsParagraph)
				{
					cache->graphicsParagraph->OpenCaret(lastCaret.column, lastCaretColor, lastCaretFrontSide);
				}
			}

			void GuiDocumentElement::GuiDocumentElementRenderer::CloseCaret(TextPos caret)
			{
				if(lastCaret!=TextPos(-1, -1))
				{
					if(0<=lastCaret.row && lastCaret.row<paragraphCaches.Count())
					{
						Ptr<ParagraphCache> cache=paragraphCaches[lastCaret.row];
						if(cache && cache->graphicsParagraph)
						{
							cache->graphicsParagraph->CloseCaret();
						}
					}
				}
				lastCaret=caret;
			}

			void GuiDocumentElement::GuiDocumentElementRenderer::SetSelection(TextPos begin, TextPos end)
			{
				if(begin>end)
				{
					TextPos t=begin;
					begin=end;
					end=t;
				}
				if(begin==end)
				{
					begin=TextPos(-1, -1);
					end=TextPos(-1, -1);
				}

				for(vint i=0;i<paragraphCaches.Count();i++)
				{
					if(begin.row<=i && i<=end.row)
					{
						Ptr<ParagraphCache> cache=EnsureAndGetCache(i, false);
						vint newBegin=i==begin.row?begin.column:0;
						vint newEnd=i==end.row?end.column:cache->fullText.Length();

						if(cache->selectionBegin!=newBegin || cache->selectionEnd!=newEnd)
						{
							cache->selectionBegin=newBegin;
							cache->selectionEnd=newEnd;
							NotifyParagraphUpdated(i, 1, 1, false);
						}
					}
					else
					{
						Ptr<ParagraphCache> cache=paragraphCaches[i];
						if(cache)
						{
							if(cache->selectionBegin!=-1 || cache->selectionEnd!=-1)
							{
								cache->selectionBegin=-1;
								cache->selectionEnd=-1;
								NotifyParagraphUpdated(i, 1, 1, false);
							}
						}
					}
				}
			}

			TextPos GuiDocumentElement::GuiDocumentElementRenderer::CalculateCaret(TextPos comparingCaret, IGuiGraphicsParagraph::CaretRelativePosition position, bool& preferFrontSide)
			{
				Ptr<ParagraphCache> cache=EnsureAndGetCache(comparingCaret.row, true);
				if(cache)
				{
					switch(position)
					{
					case IGuiGraphicsParagraph::CaretFirst:
						{
							preferFrontSide=false;
							vint caret=cache->graphicsParagraph->GetCaret(0, IGuiGraphicsParagraph::CaretFirst, preferFrontSide);
							return TextPos(comparingCaret.row, caret);
						}
					case IGuiGraphicsParagraph::CaretLast:
						{
							preferFrontSide=true;
							vint caret=cache->graphicsParagraph->GetCaret(0, IGuiGraphicsParagraph::CaretLast, preferFrontSide);
							return TextPos(comparingCaret.row, caret);
						}
					case IGuiGraphicsParagraph::CaretLineFirst:
						{
							preferFrontSide=false;
							vint caret=cache->graphicsParagraph->GetCaret(comparingCaret.column, IGuiGraphicsParagraph::CaretLineFirst, preferFrontSide);
							return TextPos(comparingCaret.row, caret);
						}
					case IGuiGraphicsParagraph::CaretLineLast:
						{
							preferFrontSide=true;
							vint caret=cache->graphicsParagraph->GetCaret(comparingCaret.column, IGuiGraphicsParagraph::CaretLineLast, preferFrontSide);
							return TextPos(comparingCaret.row, caret);
						}
					case IGuiGraphicsParagraph::CaretMoveUp:
						{
							vint caret=cache->graphicsParagraph->GetCaret(comparingCaret.column, IGuiGraphicsParagraph::CaretMoveUp, preferFrontSide);
							if(caret==comparingCaret.column && comparingCaret.row>0)
							{
								Rect caretBounds=cache->graphicsParagraph->GetCaretBounds(comparingCaret.column, preferFrontSide);
								Ptr<ParagraphCache> anotherCache=EnsureAndGetCache(comparingCaret.row-1, true);
								vint height=anotherCache->graphicsParagraph->GetHeight();
								caret=anotherCache->graphicsParagraph->GetCaretFromPoint(Point(caretBounds.x1, height));
								return TextPos(comparingCaret.row-1, caret);
							}
							else
							{
								return TextPos(comparingCaret.row, caret);
							}
						}
					case IGuiGraphicsParagraph::CaretMoveDown:
						{
							vint caret=cache->graphicsParagraph->GetCaret(comparingCaret.column, IGuiGraphicsParagraph::CaretMoveDown, preferFrontSide);
							if(caret==comparingCaret.column && comparingCaret.row<paragraphCaches.Count()-1)
							{
								Rect caretBounds=cache->graphicsParagraph->GetCaretBounds(comparingCaret.column, preferFrontSide);
								Ptr<ParagraphCache> anotherCache=EnsureAndGetCache(comparingCaret.row+1, true);
								caret=anotherCache->graphicsParagraph->GetCaretFromPoint(Point(caretBounds.x1, 0));
								return TextPos(comparingCaret.row+1, caret);
							}
							else
							{
								return TextPos(comparingCaret.row, caret);
							}
						}
					case IGuiGraphicsParagraph::CaretMoveLeft:
						{
							preferFrontSide=false;
							vint caret=cache->graphicsParagraph->GetCaret(comparingCaret.column, IGuiGraphicsParagraph::CaretMoveLeft, preferFrontSide);
							if(caret==comparingCaret.column && comparingCaret.row>0)
							{
								Ptr<ParagraphCache> anotherCache=EnsureAndGetCache(comparingCaret.row-1, true);
								caret=anotherCache->graphicsParagraph->GetCaret(0, IGuiGraphicsParagraph::CaretLast, preferFrontSide);
								return TextPos(comparingCaret.row-1, caret);
							}
							else
							{
								return TextPos(comparingCaret.row, caret);
							}
						}
					case IGuiGraphicsParagraph::CaretMoveRight:
						{
							preferFrontSide=true;
							vint caret=cache->graphicsParagraph->GetCaret(comparingCaret.column, IGuiGraphicsParagraph::CaretMoveRight, preferFrontSide);
							if(caret==comparingCaret.column && comparingCaret.row<paragraphCaches.Count()-1)
							{
								Ptr<ParagraphCache> anotherCache=EnsureAndGetCache(comparingCaret.row+1, true);
								caret=anotherCache->graphicsParagraph->GetCaret(0, IGuiGraphicsParagraph::CaretFirst, preferFrontSide);
								return TextPos(comparingCaret.row+1, caret);
							}
							else
							{
								return TextPos(comparingCaret.row, caret);
							}
						}
					}
				}
				return comparingCaret;
			}

			TextPos GuiDocumentElement::GuiDocumentElementRenderer::CalculateCaretFromPoint(Point point)
			{
				vint top=0;
				vint index=-1;
				if(GetParagraphIndexFromPoint(point, top, index))
				{
					Ptr<ParagraphCache> cache=EnsureAndGetCache(index, true);
					Point paragraphPoint(point.x, point.y-top);
					vint caret=cache->graphicsParagraph->GetCaretFromPoint(paragraphPoint);
					return TextPos(index, caret);
				}
				return TextPos(-1, -1);
			}

			Rect GuiDocumentElement::GuiDocumentElementRenderer::GetCaretBounds(TextPos caret, bool frontSide)
			{
				Ptr<ParagraphCache> cache=EnsureAndGetCache(caret.row, true);
				if(cache)
				{
					Rect bounds=cache->graphicsParagraph->GetCaretBounds(caret.column, frontSide);
					if(bounds!=Rect())
					{
						vint y=0;
						for(vint i=0;i<caret.row;i++)
						{
							EnsureAndGetCache(i, true);
							y+=paragraphHeights[i]+paragraphDistance;
						}

						bounds.y1+=y;
						bounds.y2+=y;
						return bounds;
					}
				}
				return Rect();
			}

/***********************************************************************
GuiDocumentElement
***********************************************************************/

			void GuiDocumentElement::UpdateCaret()
			{
				auto elementRenderer = renderer.Cast<GuiDocumentElementRenderer>();
				if (elementRenderer)
				{
					elementRenderer->SetSelection(caretBegin, caretEnd);
					if (caretVisible)
					{
						elementRenderer->OpenCaret(caretEnd, caretColor, caretFrontSide);
					}
					else
					{
						elementRenderer->CloseCaret(caretEnd);
					}
					InvokeOnCompositionStateChanged();
				}
			}

			GuiDocumentElement::GuiDocumentElement()
				:caretVisible(false)
				,caretFrontSide(false)
			{
			}

			GuiDocumentElement::ICallback* GuiDocumentElement::GetCallback()
			{
				return callback;
			}

			void GuiDocumentElement::SetCallback(ICallback* value)
			{
				callback = value;
			}

			Ptr<DocumentModel> GuiDocumentElement::GetDocument()
			{
				return document;
			}

			void GuiDocumentElement::SetDocument(Ptr<DocumentModel> value)
			{
				document=value;
				InvokeOnElementStateChanged();
				SetCaret(TextPos(), TextPos(), false);
			}

			TextPos GuiDocumentElement::GetCaretBegin()
			{
				return caretBegin;
			}

			TextPos GuiDocumentElement::GetCaretEnd()
			{
				return caretEnd;
			}

			bool GuiDocumentElement::IsCaretEndPreferFrontSide()
			{
				return caretFrontSide;
			}

			void GuiDocumentElement::SetCaret(TextPos begin, TextPos end, bool frontSide)
			{
				caretBegin=begin;
				caretEnd=end;
				if(caretBegin<caretEnd)
				{
					caretFrontSide=true;
				}
				else if(caretBegin>caretEnd)
				{
					caretFrontSide=false;
				}
				else
				{
					caretFrontSide=frontSide;
				}
				UpdateCaret();
			}

			bool GuiDocumentElement::GetCaretVisible()
			{
				return caretVisible;
			}

			void GuiDocumentElement::SetCaretVisible(bool value)
			{
				caretVisible=value;
				UpdateCaret();
			}

			Color GuiDocumentElement::GetCaretColor()
			{
				return caretColor;
			}

			void GuiDocumentElement::SetCaretColor(Color value)
			{
				caretColor=value;
				UpdateCaret();
			}

			TextPos GuiDocumentElement::CalculateCaret(TextPos comparingCaret, IGuiGraphicsParagraph::CaretRelativePosition position, bool& preferFrontSide)
			{
				if (auto elementRenderer = renderer.Cast<GuiDocumentElementRenderer>())
				{
					TextPos caret=elementRenderer->CalculateCaret(comparingCaret, position, preferFrontSide);
					return caret.column==-1?comparingCaret:caret;
				}
				else
				{
					return comparingCaret;
				}
			}

			TextPos GuiDocumentElement::CalculateCaretFromPoint(Point point)
			{
				if (auto elementRenderer = renderer.Cast<GuiDocumentElementRenderer>())
				{
					return elementRenderer->CalculateCaretFromPoint(point);
				}
				else
				{
					return TextPos(0, 0);
				}
			}

			Rect GuiDocumentElement::GetCaretBounds(TextPos caret, bool frontSide)
			{
				if (auto elementRenderer = renderer.Cast<GuiDocumentElementRenderer>())
				{
					return elementRenderer->GetCaretBounds(caret, frontSide);
				}
				else
				{
					return Rect();
				}
			}
			
			void GuiDocumentElement::NotifyParagraphUpdated(vint index, vint oldCount, vint newCount, bool updatedText)
			{
				if (auto elementRenderer = renderer.Cast<GuiDocumentElementRenderer>())
				{
					elementRenderer->NotifyParagraphUpdated(index, oldCount, newCount, updatedText);
					InvokeOnCompositionStateChanged();
				}
			}

			void GuiDocumentElement::EditRun(TextPos begin, TextPos end, Ptr<DocumentModel> model, bool copy)
			{
				if (auto elementRenderer = renderer.Cast<GuiDocumentElementRenderer>())
				{
					if (begin > end)
					{
						TextPos temp = begin;
						begin = end;
						end = temp;
					}

					vint newRows = document->EditRun(begin, end, model, copy);
					if (newRows != -1)
					{
						elementRenderer->NotifyParagraphUpdated(begin.row, end.row - begin.row + 1, newRows, true);
					}
					InvokeOnCompositionStateChanged();
				}
			}

			void GuiDocumentElement::EditText(TextPos begin, TextPos end, bool frontSide, const collections::Array<WString>& text)
			{
				if (auto elementRenderer = renderer.Cast<GuiDocumentElementRenderer>())
				{
					if (begin > end)
					{
						TextPos temp = begin;
						begin = end;
						end = temp;
					}

					vint newRows = document->EditText(begin, end, frontSide, text);
					if (newRows != -1)
					{
						elementRenderer->NotifyParagraphUpdated(begin.row, end.row - begin.row + 1, newRows, true);
					}
					InvokeOnCompositionStateChanged();
				}
			}

			void GuiDocumentElement::EditStyle(TextPos begin, TextPos end, Ptr<DocumentStyleProperties> style)
			{
				if (auto elementRenderer = renderer.Cast<GuiDocumentElementRenderer>())
				{
					if (begin > end)
					{
						TextPos temp = begin;
						begin = end;
						end = temp;
					}

					if (document->EditStyle(begin, end, style))
					{
						elementRenderer->NotifyParagraphUpdated(begin.row, end.row - begin.row + 1, end.row - begin.row + 1, false);
					}
					InvokeOnCompositionStateChanged();
				}
			}

			void GuiDocumentElement::EditImage(TextPos begin, TextPos end, Ptr<GuiImageData> image)
			{
				if (auto elementRenderer = renderer.Cast<GuiDocumentElementRenderer>())
				{
					if (begin > end)
					{
						TextPos temp = begin;
						begin = end;
						end = temp;
					}

					if (document->EditImage(begin, end, image))
					{
						elementRenderer->NotifyParagraphUpdated(begin.row, end.row - begin.row + 1, 1, true);
					}
					InvokeOnCompositionStateChanged();
				}
			}

			void GuiDocumentElement::EditHyperlink(vint paragraphIndex, vint begin, vint end, const WString& reference, const WString& normalStyleName, const WString& activeStyleName)
			{
				if (auto elementRenderer = renderer.Cast<GuiDocumentElementRenderer>())
				{
					if (begin > end)
					{
						vint temp = begin;
						begin = end;
						end = temp;
					}

					if (document->EditHyperlink(paragraphIndex, begin, end, reference, normalStyleName, activeStyleName))
					{
						elementRenderer->NotifyParagraphUpdated(paragraphIndex, 1, 1, false);
					}
					InvokeOnCompositionStateChanged();
				}
			}

			void GuiDocumentElement::RemoveHyperlink(vint paragraphIndex, vint begin, vint end)
			{
				if (auto elementRenderer = renderer.Cast<GuiDocumentElementRenderer>())
				{
					if (begin > end)
					{
						vint temp = begin;
						begin = end;
						end = temp;
					}

					if (document->RemoveHyperlink(paragraphIndex, begin, end))
					{
						elementRenderer->NotifyParagraphUpdated(paragraphIndex, 1, 1, false);
					}
					InvokeOnCompositionStateChanged();
				}
			}

			void GuiDocumentElement::EditStyleName(TextPos begin, TextPos end, const WString& styleName)
			{
				if (auto elementRenderer = renderer.Cast<GuiDocumentElementRenderer>())
				{
					if (begin > end)
					{
						TextPos temp = begin;
						begin = end;
						end = temp;
					}

					if (document->EditStyleName(begin, end, styleName))
					{
						elementRenderer->NotifyParagraphUpdated(begin.row, end.row - begin.row + 1, end.row - begin.row + 1, false);
					}
					InvokeOnCompositionStateChanged();
				}
			}

			void GuiDocumentElement::RemoveStyleName(TextPos begin, TextPos end)
			{
				if (auto elementRenderer = renderer.Cast<GuiDocumentElementRenderer>())
				{
					if (begin > end)
					{
						TextPos temp = begin;
						begin = end;
						end = temp;
					}

					if (document->RemoveStyleName(begin, end))
					{
						elementRenderer->NotifyParagraphUpdated(begin.row, end.row - begin.row + 1, end.row - begin.row + 1, false);
					}
					InvokeOnCompositionStateChanged();
				}
			}

			void GuiDocumentElement::RenameStyle(const WString& oldStyleName, const WString& newStyleName)
			{
				if (auto elementRenderer = renderer.Cast<GuiDocumentElementRenderer>())
				{
					document->RenameStyle(oldStyleName, newStyleName);
				}
			}

			void GuiDocumentElement::ClearStyle(TextPos begin, TextPos end)
			{
				if (auto elementRenderer = renderer.Cast<GuiDocumentElementRenderer>())
				{
					if (begin > end)
					{
						TextPos temp = begin;
						begin = end;
						end = temp;
					}

					if (document->ClearStyle(begin, end))
					{
						elementRenderer->NotifyParagraphUpdated(begin.row, end.row - begin.row + 1, end.row - begin.row + 1, false);
					}
					InvokeOnCompositionStateChanged();
				}
			}

			Ptr<DocumentStyleProperties> GuiDocumentElement::SummarizeStyle(TextPos begin, TextPos end)
			{
				if (auto elementRenderer = renderer.Cast<GuiDocumentElementRenderer>())
				{
					if (begin > end)
					{
						TextPos temp = begin;
						begin = end;
						end = temp;
					}

					return document->SummarizeStyle(begin, end);
				}
				return nullptr;
			}

			Nullable<WString> GuiDocumentElement::SummarizeStyleName(TextPos begin, TextPos end)
			{
				if (auto elementRenderer = renderer.Cast<GuiDocumentElementRenderer>())
				{
					if (begin > end)
					{
						TextPos temp = begin;
						begin = end;
						end = temp;
					}

					return document->SummarizeStyleName(begin, end);
				}
				return {};
			}

			void GuiDocumentElement::SetParagraphAlignment(TextPos begin, TextPos end, const collections::Array<Nullable<Alignment>>& alignments)
			{
				if (auto elementRenderer = renderer.Cast<GuiDocumentElementRenderer>())
				{
					vint first = begin.row;
					vint last = end.row;
					if (first > last)
					{
						vint temp = first;
						first = last;
						last = temp;
					}

					if (0 <= first && first < document->paragraphs.Count() && 0 <= last && last < document->paragraphs.Count() && last - first + 1 == alignments.Count())
					{
						for (vint i = first; i <= last; i++)
						{
							document->paragraphs[i]->alignment = alignments[i - first];
						}
						elementRenderer->NotifyParagraphUpdated(first, alignments.Count(), alignments.Count(), false);
					}
					InvokeOnCompositionStateChanged();
				}
			}

			Nullable<Alignment> GuiDocumentElement::SummarizeParagraphAlignment(TextPos begin, TextPos end)
			{
				if (auto elementRenderer = renderer.Cast<GuiDocumentElementRenderer>())
				{
					if (begin > end)
					{
						TextPos temp = begin;
						begin = end;
						end = temp;
					}

					return document->SummarizeParagraphAlignment(begin, end);
				}
				return {};
			}

			Ptr<DocumentHyperlinkRun::Package> GuiDocumentElement::GetHyperlinkFromPoint(Point point)
			{
				if (auto elementRenderer = renderer.Cast<GuiDocumentElementRenderer>())
				{
					return elementRenderer->GetHyperlinkFromPoint(point);
				}
				return nullptr;
			}
		}
	}
}

/***********************************************************************
.\GRAPHICSELEMENT\GUIGRAPHICSELEMENT.CPP
***********************************************************************/

namespace vl
{
	namespace presentation
	{
		namespace elements
		{
			using namespace collections;

/***********************************************************************
GuiSolidBorderElement
***********************************************************************/

			GuiSolidBorderElement::GuiSolidBorderElement()
				:color(0, 0, 0)
			{
			}

			Color GuiSolidBorderElement::GetColor()
			{
				return color;
			}

			void GuiSolidBorderElement::SetColor(Color value)
			{
				if(color!=value)
				{
					color=value;
					InvokeOnElementStateChanged();
				}
			}
			
			ElementShape GuiSolidBorderElement::GetShape()
			{
				return shape;
			}

			void GuiSolidBorderElement::SetShape(ElementShape value)
			{
				shape=value;
			}

/***********************************************************************
Gui3DBorderElement
***********************************************************************/

			Gui3DBorderElement::Gui3DBorderElement()
			{
			}

			Color Gui3DBorderElement::GetColor1()
			{
				return color1;
			}

			void Gui3DBorderElement::SetColor1(Color value)
			{
				SetColors(value, color2);
			}

			Color Gui3DBorderElement::GetColor2()
			{
				return color2;
			}

			void Gui3DBorderElement::SetColor2(Color value)
			{
				SetColors(color1, value);
			}

			void Gui3DBorderElement::SetColors(Color value1, Color value2)
			{
				if(color1!=value1 || color2!=value2)
				{
					color1=value1;
					color2=value2;
					InvokeOnElementStateChanged();
				}
			}

/***********************************************************************
Gui3DSplitterElement
***********************************************************************/

			Gui3DSplitterElement::Gui3DSplitterElement()
				:direction(Horizontal)
			{
			}

			Color Gui3DSplitterElement::GetColor1()
			{
				return color1;
			}

			void Gui3DSplitterElement::SetColor1(Color value)
			{
				SetColors(value, color2);
			}

			Color Gui3DSplitterElement::GetColor2()
			{
				return color2;
			}

			void Gui3DSplitterElement::SetColor2(Color value)
			{
				SetColors(color1, value);
			}

			void Gui3DSplitterElement::SetColors(Color value1, Color value2)
			{
				if(color1!=value1 || color2!=value2)
				{
					color1=value1;
					color2=value2;
					InvokeOnElementStateChanged();
				}
			}

			Gui3DSplitterElement::Direction Gui3DSplitterElement::GetDirection()
			{
				return direction;
			}

			void Gui3DSplitterElement::SetDirection(Direction value)
			{
				if(direction!=value)
				{
					direction=value;
					InvokeOnElementStateChanged();
				}
			}

/***********************************************************************
GuiSolidBackgroundElement
***********************************************************************/

			GuiSolidBackgroundElement::GuiSolidBackgroundElement()
				:color(255, 255, 255)
			{
			}

			Color GuiSolidBackgroundElement::GetColor()
			{
				return color;
			}

			void GuiSolidBackgroundElement::SetColor(Color value)
			{
				if(color!=value)
				{
					color=value;
					InvokeOnElementStateChanged();
				}
			}
			
			ElementShape GuiSolidBackgroundElement::GetShape()
			{
				return shape;
			}

			void GuiSolidBackgroundElement::SetShape(ElementShape value)
			{
				shape=value;
			}

/***********************************************************************
GuiGradientBackgroundElement
***********************************************************************/

			GuiGradientBackgroundElement::GuiGradientBackgroundElement()
				:direction(Horizontal)
			{
			}

			Color GuiGradientBackgroundElement::GetColor1()
			{
				return color1;
			}

			void GuiGradientBackgroundElement::SetColor1(Color value)
			{
				SetColors(value, color2);
			}

			Color GuiGradientBackgroundElement::GetColor2()
			{
				return color2;
			}

			void GuiGradientBackgroundElement::SetColor2(Color value)
			{
				SetColors(color1, value);
			}

			void GuiGradientBackgroundElement::SetColors(Color value1, Color value2)
			{
				if(color1!=value1 || color2!=value2)
				{
					color1=value1;
					color2=value2;
					InvokeOnElementStateChanged();
				}
			}

			GuiGradientBackgroundElement::Direction GuiGradientBackgroundElement::GetDirection()
			{
				return direction;
			}

			void GuiGradientBackgroundElement::SetDirection(Direction value)
			{
				if(direction!=value)
				{
					direction=value;
					InvokeOnElementStateChanged();
				}
			}
			
			ElementShape GuiGradientBackgroundElement::GetShape()
			{
				return shape;
			}

			void GuiGradientBackgroundElement::SetShape(ElementShape value)
			{
				shape=value;
			}

/***********************************************************************
GuiInnerShadowElement
***********************************************************************/

			GuiInnerShadowElement::GuiInnerShadowElement()
			{
			}

			Color GuiInnerShadowElement::GetColor()
			{
				return color;
			}

			void GuiInnerShadowElement::SetColor(Color value)
			{
				if (color != value)
				{
					color = value;
					InvokeOnElementStateChanged();
				}
			}

			vint GuiInnerShadowElement::GetThickness()
			{
				return thickness;
			}

			void GuiInnerShadowElement::SetThickness(vint value)
			{
				if (thickness != value)
				{
					thickness = value;
					InvokeOnElementStateChanged();
				}
			}

/***********************************************************************
GuiSolidLabelElement
***********************************************************************/

			GuiSolidLabelElement::GuiSolidLabelElement()
				:color(0, 0, 0)
				,hAlignment(Alignment::Left)
				,vAlignment(Alignment::Top)
				,wrapLine(false)
				,ellipse(false)
				,multiline(false)
				,wrapLineHeightCalculation(false)
			{
				fontProperties.fontFamily=L"Lucida Console";
				fontProperties.size=12;
			}

			Color GuiSolidLabelElement::GetColor()
			{
				return color;
			}

			void GuiSolidLabelElement::SetColor(Color value)
			{
				if(color!=value)
				{
					color=value;
					InvokeOnElementStateChanged();
				}
			}

			const FontProperties& GuiSolidLabelElement::GetFont()
			{
				return fontProperties;
			}

			void GuiSolidLabelElement::SetFont(const FontProperties& value)
			{
				if(fontProperties!=value)
				{
					fontProperties=value;
					InvokeOnElementStateChanged();
				}
			}

			const WString& GuiSolidLabelElement::GetText()
			{
				return text;
			}

			void GuiSolidLabelElement::SetText(const WString& value)
			{
				if(text!=value)
				{
					text=value;
					InvokeOnElementStateChanged();
				}
			}

			Alignment GuiSolidLabelElement::GetHorizontalAlignment()
			{
				return hAlignment;
			}

			Alignment GuiSolidLabelElement::GetVerticalAlignment()
			{
				return vAlignment;
			}

			void GuiSolidLabelElement::SetHorizontalAlignment(Alignment value)
			{
				SetAlignments(value, vAlignment);
			}

			void GuiSolidLabelElement::SetVerticalAlignment(Alignment value)
			{
				SetAlignments(hAlignment, value);
			}

			void GuiSolidLabelElement::SetAlignments(Alignment horizontal, Alignment vertical)
			{
				if(hAlignment!=horizontal || vAlignment!=vertical)
				{
					hAlignment=horizontal;
					vAlignment=vertical;
					InvokeOnElementStateChanged();
				}
			}

			bool GuiSolidLabelElement::GetWrapLine()
			{
				return wrapLine;
			}

			void GuiSolidLabelElement::SetWrapLine(bool value)
			{
				if(wrapLine!=value)
				{
					wrapLine=value;
					InvokeOnElementStateChanged();
				}
			}

			bool GuiSolidLabelElement::GetEllipse()
			{
				return ellipse;
			}

			void GuiSolidLabelElement::SetEllipse(bool value)
			{
				if(ellipse!=value)
				{
					ellipse=value;
					InvokeOnElementStateChanged();
				}
			}

			bool GuiSolidLabelElement::GetMultiline()
			{
				return multiline;
			}

			void GuiSolidLabelElement::SetMultiline(bool value)
			{
				if(multiline!=value)
				{
					multiline=value;
					InvokeOnElementStateChanged();
				}
			}

			bool GuiSolidLabelElement::GetWrapLineHeightCalculation()
			{
				return wrapLineHeightCalculation;
			}

			void GuiSolidLabelElement::SetWrapLineHeightCalculation(bool value)
			{
				if(wrapLineHeightCalculation!=value)
				{
					wrapLineHeightCalculation=value;
					InvokeOnElementStateChanged();
				}
			}

/***********************************************************************
GuiImageFrameElement
***********************************************************************/

			GuiImageFrameElement::GuiImageFrameElement()
				:frameIndex(0)
				,hAlignment(Alignment::Left)
				,vAlignment(Alignment::Top)
				,stretch(false)
				,enabled(true)
			{
			}

			Ptr<INativeImage> GuiImageFrameElement::GetImage()
			{
				return image;
			}

			vint GuiImageFrameElement::GetFrameIndex()
			{
				return frameIndex;
			}

			void GuiImageFrameElement::SetImage(Ptr<INativeImage> value)
			{
				SetImage(value, frameIndex);
			}

			void GuiImageFrameElement::SetFrameIndex(vint value)
			{
				SetImage(image, value);
			}

			void GuiImageFrameElement::SetImage(Ptr<INativeImage> _image, vint _frameIndex)
			{
				if(image!=_image || frameIndex!=_frameIndex)
				{
					if(!_image)
					{
						image=0;
						frameIndex=0;
					}
					else if(0<=_frameIndex && _frameIndex<_image->GetFrameCount())
					{
						image=_image;
						frameIndex=_frameIndex;
					}
					InvokeOnElementStateChanged();
				}
			}

			Alignment GuiImageFrameElement::GetHorizontalAlignment()
			{
				return hAlignment;
			}

			Alignment GuiImageFrameElement::GetVerticalAlignment()
			{
				return vAlignment;
			}

			void GuiImageFrameElement::SetHorizontalAlignment(Alignment value)
			{
				SetAlignments(value, vAlignment);
			}

			void GuiImageFrameElement::SetVerticalAlignment(Alignment value)
			{
				SetAlignments(hAlignment, value);
			}

			void GuiImageFrameElement::SetAlignments(Alignment horizontal, Alignment vertical)
			{
				if(hAlignment!=horizontal || vAlignment!=vertical)
				{
					hAlignment=horizontal;
					vAlignment=vertical;
					InvokeOnElementStateChanged();
				}
			}

			bool GuiImageFrameElement::GetStretch()
			{
				return stretch;
			}

			void GuiImageFrameElement::SetStretch(bool value)
			{
				if(stretch!=value)
				{
					stretch=value;
					InvokeOnElementStateChanged();
				}
			}

			bool GuiImageFrameElement::GetEnabled()
			{
				return enabled;
			}

			void GuiImageFrameElement::SetEnabled(bool value)
			{
				if(enabled!=value)
				{
					enabled=value;
					InvokeOnElementStateChanged();
				}
			}

/***********************************************************************
GuiPolygonElement
***********************************************************************/

			GuiPolygonElement::GuiPolygonElement()
			{
			}

			Size GuiPolygonElement::GetSize()
			{
				return size;
			}

			void GuiPolygonElement::SetSize(Size value)
			{
				if(size!=value)
				{
					size=value;
					InvokeOnElementStateChanged();
				}
			}

			const Point& GuiPolygonElement::GetPoint(vint index)
			{
				return points[index];
			}

			vint GuiPolygonElement::GetPointCount()
			{
				return points.Count();
			}

			void GuiPolygonElement::SetPoints(const Point* p, vint count)
			{
				points.Resize(count);
				if(count>0)
				{
					memcpy(&points[0], p, sizeof(*p)*count);
				}
				InvokeOnElementStateChanged();
			}

			const GuiPolygonElement::PointArray& GuiPolygonElement::GetPointsArray()
			{
				return points;
			}

			void GuiPolygonElement::SetPointsArray(const PointArray& value)
			{
				CopyFrom(points, value);
				InvokeOnElementStateChanged();
			}

			Color GuiPolygonElement::GetBorderColor()
			{
				return borderColor;
			}

			void GuiPolygonElement::SetBorderColor(Color value)
			{
				if(borderColor!=value)
				{
					borderColor=value;
					InvokeOnElementStateChanged();
				}
			}

			Color GuiPolygonElement::GetBackgroundColor()
			{
				return backgroundColor;
			}

			void GuiPolygonElement::SetBackgroundColor(Color value)
			{
				if(backgroundColor!=value)
				{
					backgroundColor=value;
					InvokeOnElementStateChanged();
				}
			}
		}
	}
}

/***********************************************************************
.\GRAPHICSELEMENT\GUIGRAPHICSRESOURCEMANAGER.CPP
***********************************************************************/

namespace vl
{
	namespace presentation
	{
		namespace elements
		{
			using namespace collections;

/***********************************************************************
GuiGraphicsResourceManager
***********************************************************************/

			GuiGraphicsResourceManager::GuiGraphicsResourceManager()
			{
			}

			GuiGraphicsResourceManager::~GuiGraphicsResourceManager()
			{
			}

			bool GuiGraphicsResourceManager::RegisterElementFactory(IGuiGraphicsElementFactory* factory)
			{
				if(elementFactories.Keys().Contains(factory->GetElementTypeName()))
				{
					return false;
				}
				else
				{
					elementFactories.Add(factory->GetElementTypeName(), factory);
					return true;
				}
			}

			bool GuiGraphicsResourceManager::RegisterRendererFactory(const WString& elementTypeName, IGuiGraphicsRendererFactory* factory)
			{
				if(rendererFactories.Keys().Contains(elementTypeName))
				{
					return false;
				}
				else
				{
					rendererFactories.Add(elementTypeName, factory);
					return true;
				}
			}

			IGuiGraphicsElementFactory* GuiGraphicsResourceManager::GetElementFactory(const WString& elementTypeName)
			{
				vint index=elementFactories.Keys().IndexOf(elementTypeName);
				return index==-1?0:elementFactories.Values().Get(index).Obj();
			}

			IGuiGraphicsRendererFactory* GuiGraphicsResourceManager::GetRendererFactory(const WString& elementTypeName)
			{
				vint index=rendererFactories.Keys().IndexOf(elementTypeName);
				return index==-1?nullptr:rendererFactories.Values().Get(index).Obj();
			}

			GuiGraphicsResourceManager* guiGraphicsResourceManager=0;

			GuiGraphicsResourceManager* GetGuiGraphicsResourceManager()
			{
				return guiGraphicsResourceManager;
			}

			void SetGuiGraphicsResourceManager(GuiGraphicsResourceManager* resourceManager)
			{
				guiGraphicsResourceManager=resourceManager;
			}

			bool RegisterFactories(IGuiGraphicsElementFactory* elementFactory, IGuiGraphicsRendererFactory* rendererFactory)
			{
				if(guiGraphicsResourceManager && elementFactory && rendererFactory)
				{
					if(guiGraphicsResourceManager->RegisterElementFactory(elementFactory))
					{
						if(guiGraphicsResourceManager->RegisterRendererFactory(elementFactory->GetElementTypeName(), rendererFactory))
						{
							return true;
						}
					}
				}
				return false;
			}
		}
	}
}

/***********************************************************************
.\GRAPHICSELEMENT\GUIGRAPHICSTEXTELEMENT.CPP
***********************************************************************/

namespace vl
{
	using namespace collections;

	namespace presentation
	{
		namespace elements
		{
			namespace text
			{

/***********************************************************************
text::TextLine
***********************************************************************/

				TextLine::TextLine()
					:text(0)
					,att(0)
					,availableOffsetCount(0)
					,bufferLength(0)
					,dataLength(0)
					,lexerFinalState(-1)
					,contextFinalState(-1)
				{
				}

				TextLine::~TextLine()
				{
				}

				vint TextLine::CalculateBufferLength(vint dataLength)
				{
					if(dataLength<1)dataLength=1;
					vint bufferLength=dataLength-dataLength%BlockSize;
					if(bufferLength<dataLength)
					{
						bufferLength+=BlockSize;
					}
					return bufferLength;
				}

				void TextLine::Initialize()
				{
					Finalize();
					text=new wchar_t[BlockSize];
					att=new CharAtt[BlockSize];
					bufferLength=BlockSize;

					memset(text, 0, sizeof(wchar_t)*bufferLength);
					memset(att, 0, sizeof(CharAtt)*bufferLength);
				}

				void TextLine::Finalize()
				{
					if(text)
					{
						delete[] text;
						text=0;
					}
					if(att)
					{
						delete[] att;
						att=0;
					}
					availableOffsetCount=0;
					bufferLength=0;
					dataLength=0;
				}

				bool TextLine::IsReady()
				{
					return text && att;
				}

				bool TextLine::Modify(vint start, vint count, const wchar_t* input, vint inputCount)
				{
					if(!text || !att || start<0 || count<0 || start+count>dataLength || inputCount<0) return false;

					vint newDataLength=dataLength-count+inputCount;
					vint newBufferLength=CalculateBufferLength(newDataLength);
					if(newBufferLength!=bufferLength)
					{
						wchar_t* newText=new wchar_t[newBufferLength];
						memcpy(newText, text, start*sizeof(wchar_t));
						memcpy(newText+start, input, inputCount*sizeof(wchar_t));
						memcpy(newText+start+inputCount, text+start+count, (dataLength-start-count)*sizeof(wchar_t));

						CharAtt* newAtt=new CharAtt[newBufferLength];
						memcpy(newAtt, att, start*sizeof(CharAtt));
						memset(newAtt+start, 0, inputCount*sizeof(CharAtt));
						memcpy(newAtt+start+inputCount, att+start+count, (dataLength-start-count)*sizeof(CharAtt));

						delete[] text;
						delete[] att;
						text=newText;
						att=newAtt;
					}
					else
					{
						memmove(text+start+inputCount, text+start+count, (dataLength-start-count)*sizeof(wchar_t));
						memmove(att+start+inputCount, att+start+count, (dataLength-start-count)*sizeof(CharAtt));
						memcpy(text+start, input, inputCount*sizeof(wchar_t));
						memset(att+start, 0, inputCount*sizeof(CharAtt));
					}
					dataLength=newDataLength;
					bufferLength=newBufferLength;
					if(availableOffsetCount>start)
					{
						availableOffsetCount=start;
					}

					return true;
				}

				TextLine TextLine::Split(vint index)
				{
					if(index<0 || index>dataLength) return TextLine();
					vint count=dataLength-index;
					TextLine line;
					line.Initialize();
					line.Modify(0, 0, text+index, count);
					memcpy(line.att, att+index, count*sizeof(CharAtt));
					Modify(index, count, L"", 0);
					return line;
				}

				void TextLine::AppendAndFinalize(TextLine& line)
				{
					vint oldDataLength=dataLength;
					Modify(oldDataLength, 0, line.text, line.dataLength);
					memcpy(att+oldDataLength, line.att, line.dataLength*sizeof(CharAtt));
					line.Finalize();
				}

/***********************************************************************
text::CharMeasurer
***********************************************************************/

				CharMeasurer::CharMeasurer(vint _rowHeight)
					:oldRenderTarget(0)
					,rowHeight(_rowHeight)
				{
					memset(widths, 0, sizeof(widths));
				}

				CharMeasurer::~CharMeasurer()
				{
				}

				void CharMeasurer::SetRenderTarget(IGuiGraphicsRenderTarget* value)
				{
					if(oldRenderTarget!=value)
					{
						oldRenderTarget=value;
						rowHeight=GetRowHeightInternal(oldRenderTarget);
						memset(widths, 0, sizeof(widths));
					}
				}

				vint CharMeasurer::MeasureWidth(wchar_t character)
				{
					vint w=widths[character];
					if(w==0)
					{
						widths[character]=w=MeasureWidthInternal(character, oldRenderTarget);
					}
					return w;
				}

				vint CharMeasurer::GetRowHeight()
				{
					return rowHeight;
				}

/***********************************************************************
text::TextLines
***********************************************************************/

				TextLines::TextLines(GuiColorizedTextElement* _ownerElement)
					:ownerElement(_ownerElement)
					,charMeasurer(0)
					,renderTarget(0)
					,tabWidth(1)
					,tabSpaceCount(4)
					,passwordChar(L'\0')
				{
					TextLine line;
					line.Initialize();
					lines.Add(line);
				}

				TextLines::~TextLines()
				{
					RemoveLines(0, lines.Count());
				}

				//--------------------------------------------------------

				vint TextLines::GetCount()
				{
					return lines.Count();
				}

				TextLine& TextLines::GetLine(vint row)
				{
					return lines[row];
				}

				CharMeasurer* TextLines::GetCharMeasurer()
				{
					return charMeasurer;
				}

				void TextLines::SetCharMeasurer(CharMeasurer* value)
				{
					charMeasurer=value;
					if(charMeasurer) charMeasurer->SetRenderTarget(renderTarget);
					ClearMeasurement();
				}

				IGuiGraphicsRenderTarget* TextLines::GetRenderTarget()
				{
					return renderTarget;
				}

				void TextLines::SetRenderTarget(IGuiGraphicsRenderTarget* value)
				{
					renderTarget=value;
					if(charMeasurer) charMeasurer->SetRenderTarget(renderTarget);
					ClearMeasurement();
				}

				WString TextLines::GetText(TextPos start, TextPos end)
				{
					if(!IsAvailable(start) || !IsAvailable(end) || start>end) return L"";

					if(start.row==end.row)
					{
						return WString(lines[start.row].text+start.column, end.column-start.column);
					}

					vint count=0;
					for(vint i=start.row+1;i<end.row;i++)
					{
						count+=lines[i].dataLength;
					}
					count+=lines[start.row].dataLength-start.column;
					count+=end.column;

					Array<wchar_t> buffer;
					buffer.Resize(count+(end.row-start.row)*2);
					wchar_t* writing=&buffer[0];

					for(vint i=start.row;i<=end.row;i++)
					{
						wchar_t* text=lines[i].text;
						vint chars=0;
						if(i==start.row)
						{
							text+=start.column;
							chars=lines[i].dataLength-start.column;
						}
						else if(i==end.row)
						{
							chars=end.column;
						}
						else
						{
							chars=lines[i].dataLength;
						}

						if(i!=start.row)
						{
							*writing++=L'\r';
							*writing++=L'\n';
						}
						memcpy(writing, text, chars*sizeof(wchar_t));
						writing+=chars;
					}
					return WString(&buffer[0], buffer.Count());
				}

				WString TextLines::GetText()
				{
					return GetText(TextPos(0, 0), TextPos(lines.Count()-1, lines[lines.Count()-1].dataLength));
				}

				void TextLines::SetText(const WString& value)
				{
					Modify(TextPos(0, 0), TextPos(lines.Count()-1, lines[lines.Count()-1].dataLength), value);
				}

				//--------------------------------------------------------

				bool TextLines::RemoveLines(vint start, vint count)
				{
					if(start<0 || count<0 || start+count>lines.Count()) return false;
					for(vint i=start+count-1;i>=start;i--)
					{
						lines[i].Finalize();
					}
					lines.RemoveRange(start, count);
					return true;
				}

				bool TextLines::IsAvailable(TextPos pos)
				{
					return 0<=pos.row && pos.row<lines.Count() && 0<=pos.column && pos.column<=lines[pos.row].dataLength;
				}

				TextPos TextLines::Normalize(TextPos pos)
				{
					if(pos.row<0)
					{
						return TextPos(0, 0);
					}
					else if(pos.row>=lines.Count())
					{
						return TextPos(lines.Count()-1, lines[lines.Count()-1].dataLength);
					}
					else
					{
						TextLine& line=lines[pos.row];
						if(pos.column<0)
						{
							return TextPos(pos.row, 0);
						}
						else if(pos.column>line.dataLength)
						{
							return TextPos(pos.row, line.dataLength);
						}
						else
						{
							return pos;
						}
					}
				}

				TextPos TextLines::Modify(TextPos start, TextPos end, const wchar_t** inputs, vint* inputCounts, vint rows)
				{
					if(!IsAvailable(start) || !IsAvailable(end) || start>end) return TextPos(-1, -1);
					if (ownerElement)
					{
						ownerElement->InvokeOnElementStateChanged();
					}

					if(rows==1)
					{
						if(start.row==end.row)
						{
							lines[start.row].Modify(start.column, end.column-start.column, inputs[0], inputCounts[0]);
						}
						else
						{
							if(end.row-start.row>1)
							{
								RemoveLines(start.row+1, end.row-start.row-1);
							}
							vint modifyCount=lines[start.row].dataLength-start.column+end.column;
							lines[start.row].AppendAndFinalize(lines[start.row+1]);
							lines.RemoveAt(start.row+1);
							lines[start.row].Modify(start.column, modifyCount, inputs[0], inputCounts[0]);
						}
						return TextPos(start.row, start.column+inputCounts[0]);
					}

					if(start.row==end.row)
					{
						TextLine newLine=lines[start.row].Split(end.column);
						lines.Insert(start.row+1, newLine);
						end=TextPos(start.row+1, 0);
					}

					vint oldMiddleLines=end.row-start.row-1;
					vint newMiddleLines=rows-2;
					if(oldMiddleLines<newMiddleLines)
					{
						for(vint i=oldMiddleLines;i<newMiddleLines;i++)
						{
							TextLine line;
							line.Initialize();
							lines.Insert(end.row, line);
						}
					}
					else if(oldMiddleLines>newMiddleLines)
					{
						RemoveLines(start.row+newMiddleLines+1, oldMiddleLines-newMiddleLines);
					}
					end.row+=newMiddleLines-oldMiddleLines;

					lines[start.row].Modify(start.column, lines[start.row].dataLength-start.column, inputs[0], inputCounts[0]);
					lines[end.row].Modify(0, end.column, inputs[rows-1], inputCounts[rows-1]);
					for(vint i=1;i<rows-1;i++)
					{
						lines[start.row+i].Modify(0, lines[start.row+i].dataLength, inputs[i], inputCounts[i]);
					}
					return TextPos(end.row, inputCounts[rows-1]);
				}

				TextPos TextLines::Modify(TextPos start, TextPos end, const wchar_t* input, vint inputCount)
				{
					List<const wchar_t*> inputs;
					List<vint> inputCounts;
					const wchar_t* previous=input;
					const wchar_t* current=input;

					while(true)
					{
						if(current==input+inputCount)
						{
							inputs.Add(previous);
							inputCounts.Add(current-previous);
							break;
						}
						else if(*current==L'\r' || *current==L'\n')
						{
							inputs.Add(previous);
							inputCounts.Add(current-previous);
							previous=current+(current[1]==L'\n'?2:1);
							current=previous;
						}
						else
						{
							current++;
						}
					}

					return Modify(start, end, &inputs[0], &inputCounts[0], inputs.Count());
				}

				TextPos TextLines::Modify(TextPos start, TextPos end, const wchar_t* input)
				{
					return Modify(start, end, input, wcslen(input));
				}

				TextPos TextLines::Modify(TextPos start, TextPos end, const WString& input)
				{
					return Modify(start, end, input.Buffer(), input.Length());
				}

				void TextLines::Clear()
				{
					RemoveLines(0, lines.Count());
					TextLine line;
					line.Initialize();
					lines.Add(line);
					if (ownerElement)
					{
						ownerElement->InvokeOnElementStateChanged();
					}
				}

				//--------------------------------------------------------

				void TextLines::ClearMeasurement()
				{
					for (vint i = 0; i < lines.Count(); i++)
					{
						lines[i].availableOffsetCount = 0;
					}

					tabWidth = tabSpaceCount * (charMeasurer ? charMeasurer->MeasureWidth(L' ') : 1);
					if (tabWidth == 0)
					{
						tabWidth = 1;
					}

					if (ownerElement)
					{
						ownerElement->InvokeOnElementStateChanged();
					}
				}

				vint TextLines::GetTabSpaceCount()
				{
					return tabSpaceCount;
				}

				void TextLines::SetTabSpaceCount(vint value)
				{
					if(value<1) value=1;
					if(tabSpaceCount!=value)
					{
						tabSpaceCount=value;
						ClearMeasurement();
					}
				}

				void TextLines::MeasureRow(vint row)
				{
					TextLine& line=lines[row];
					vint offset=0;
					if(line.availableOffsetCount)
					{
						offset=line.att[line.availableOffsetCount-1].rightOffset;
					}
					for(vint i=line.availableOffsetCount;i<line.dataLength;i++)
					{
						CharAtt& att=line.att[i];
						wchar_t c=line.text[i];
						vint width=0;
						if(passwordChar)
						{
							width = charMeasurer ? charMeasurer->MeasureWidth(passwordChar) : 1;
						}
						else if(c==L'\t')
						{
							width=tabWidth-offset%tabWidth;
						}
						else
						{
							width = charMeasurer ? charMeasurer->MeasureWidth(line.text[i]) : 1;
						}
						offset+=width;
						att.rightOffset=(int)offset;
					}
					line.availableOffsetCount=line.dataLength;
				}

				vint TextLines::GetRowWidth(vint row)
				{
					if(row<0 || row>=lines.Count()) return -1;
					TextLine& line=lines[row];
					if(line.dataLength==0)
					{
						return 0;
					}
					else
					{
						MeasureRow(row);
						return line.att[line.dataLength-1].rightOffset;
					}
				}

				vint TextLines::GetRowHeight()
				{
					return charMeasurer ? charMeasurer->GetRowHeight() : 1;
				}

				vint TextLines::GetMaxWidth()
				{
					vint width=0;
					for(vint i=0;i<lines.Count();i++)
					{
						vint rowWidth=GetRowWidth(i);
						if(width<rowWidth)
						{
							width=rowWidth;
						}
					}
					return width;
				}

				vint TextLines::GetMaxHeight()
				{
					return lines.Count() * GetRowHeight();
				}

				TextPos TextLines::GetTextPosFromPoint(Point point)
				{
					vint h = GetRowHeight();
					if(point.y<0)
					{
						point.y=0;
					}
					else if(point.y>=h*lines.Count())
					{
						point.y=h*lines.Count()-1;
					}

					vint row=point.y/h;
					if(point.x<0)
					{
						return TextPos(row, 0);
					}
					else if(point.x>=GetRowWidth(row))
					{
						return TextPos(row, lines[row].dataLength);
					}
					TextLine& line=lines[row];

					vint i1=0, i2=line.dataLength;
					vint p1=0, p2=line.att[line.dataLength-1].rightOffset;
					while(i2-i1>1)
					{
						vint i=(i1+i2)/2;
						vint p=i==0?0:line.att[i-1].rightOffset;
						if(point.x<p)
						{
							i2=i;
							p2=p;
						}
						else
						{
							i1=i;
							p1=p;
						}
					}
					return TextPos(row, i1);
				}

				Point TextLines::GetPointFromTextPos(TextPos pos)
				{
					if(IsAvailable(pos))
					{
						vint y = pos.row * GetRowHeight();
						if(pos.column==0)
						{
							return Point(0, y);
						}
						else
						{
							MeasureRow(pos.row);
							TextLine& line=lines[pos.row];
							return Point(line.att[pos.column-1].rightOffset, y);
						}
					}
					else
					{
						return Point(-1, -1);
					}
				}

				Rect TextLines::GetRectFromTextPos(TextPos pos)
				{
					Point point=GetPointFromTextPos(pos);
					if(point==Point(-1, -1))
					{
						return Rect(-1, -1, -1, -1);
					}
					else
					{
						vint h = GetRowHeight();
						TextLine& line=lines[pos.row];
						if(pos.column==line.dataLength)
						{
							return Rect(point, Size(h/2, h));
						}
						else
						{
							return Rect(point, Size(line.att[pos.column].rightOffset-point.x, h));
						}
					}
				}

				//--------------------------------------------------------

				wchar_t TextLines::GetPasswordChar()
				{
					return passwordChar;
				}

				void TextLines::SetPasswordChar(wchar_t value)
				{
					passwordChar=value;
					ClearMeasurement();
				}
			}

			using namespace text;

/***********************************************************************
GuiColorizedTextElement
***********************************************************************/

			GuiColorizedTextElement::GuiColorizedTextElement()
				:callback(0)
				,isVisuallyEnabled(true)
				,isFocused(false)
				,caretVisible(false)
				,lines(this)
			{
			}

			text::TextLines& GuiColorizedTextElement::GetLines()
			{
				return lines;
			}

			GuiColorizedTextElement::ICallback* GuiColorizedTextElement::GetCallback()
			{
				return callback;
			}

			void GuiColorizedTextElement::SetCallback(ICallback* value)
			{
				callback=value;
				if(!callback)
				{
					lines.SetCharMeasurer(0);
				}
			}

			const GuiColorizedTextElement::ColorArray& GuiColorizedTextElement::GetColors()
			{
				return colors;
			}

			void GuiColorizedTextElement::SetColors(const ColorArray& value)
			{
				CopyFrom(colors, value);
				if(callback) callback->ColorChanged();
				InvokeOnElementStateChanged();
			}

			void GuiColorizedTextElement::ResetTextColorIndex(vint index)
			{
				vint lineCount = lines.GetCount();
				for (vint i = 0; i < lineCount; i++)
				{
					auto& line = lines.GetLine(i);
					line.lexerFinalState = -1;
					line.contextFinalState = -1;
					for (vint j = 0; j < line.dataLength; j++)
					{
						line.att[j].colorIndex = (vuint32_t)index;
					}
				}
			}

			const FontProperties& GuiColorizedTextElement::GetFont()
			{
				return font;
			}

			void GuiColorizedTextElement::SetFont(const FontProperties& value)
			{
				if(font!=value)
				{
					font=value;
					if(callback)
					{
						callback->FontChanged();
					}
					InvokeOnElementStateChanged();
				}
			}

			wchar_t GuiColorizedTextElement::GetPasswordChar()
			{
				return lines.GetPasswordChar();
			}

			void GuiColorizedTextElement::SetPasswordChar(wchar_t value)
			{
				if(lines.GetPasswordChar()!=value)
				{
					lines.SetPasswordChar(value);
					InvokeOnElementStateChanged();
				}
			}

			Point GuiColorizedTextElement::GetViewPosition()
			{
				return viewPosition;
			}

			void GuiColorizedTextElement::SetViewPosition(Point value)
			{
				if(viewPosition!=value)
				{
					viewPosition=value;
					InvokeOnElementStateChanged();
				}
			}

			bool GuiColorizedTextElement::GetVisuallyEnabled()
			{
				return isVisuallyEnabled;
			}

			void GuiColorizedTextElement::SetVisuallyEnabled(bool value)
			{
				if(isVisuallyEnabled!=value)
				{
					isVisuallyEnabled=value;
					InvokeOnElementStateChanged();
				}
			}

			bool GuiColorizedTextElement::GetFocused()
			{
				return isFocused;
			}

			void GuiColorizedTextElement::SetFocused(bool value)
			{
				if(isFocused!=value)
				{
					isFocused=value;
					InvokeOnElementStateChanged();
				}
			}

			TextPos GuiColorizedTextElement::GetCaretBegin()
			{
				return caretBegin;
			}

			void GuiColorizedTextElement::SetCaretBegin(TextPos value)
			{
				caretBegin=value;
				InvokeOnElementStateChanged();
			}

			TextPos GuiColorizedTextElement::GetCaretEnd()
			{
				return caretEnd;
			}

			void GuiColorizedTextElement::SetCaretEnd(TextPos value)
			{
				caretEnd=value;
				InvokeOnElementStateChanged();
			}

			bool GuiColorizedTextElement::GetCaretVisible()
			{
				return caretVisible;
			}

			void GuiColorizedTextElement::SetCaretVisible(bool value)
			{
				caretVisible=value;
				InvokeOnElementStateChanged();
			}

			Color GuiColorizedTextElement::GetCaretColor()
			{
				return caretColor;
			}

			void GuiColorizedTextElement::SetCaretColor(Color value)
			{
				if(caretColor!=value)
				{
					caretColor=value;
					InvokeOnElementStateChanged();
				}
			}
		}
	}
}

/***********************************************************************
.\NATIVEWINDOW\GUINATIVEWINDOW.CPP
***********************************************************************/

namespace vl
{
	namespace presentation
	{

/***********************************************************************
INativeWindowListener
***********************************************************************/

		INativeWindowListener::HitTestResult INativeWindowListener::HitTest(Point location)
		{
			return INativeWindowListener::NoDecision;
		}

		void INativeWindowListener::Moving(Rect& bounds, bool fixSizeOnly)
		{
		}

		void INativeWindowListener::Moved()
		{
		}

		void INativeWindowListener::Enabled()
		{
		}

		void INativeWindowListener::Disabled()
		{
		}

		void INativeWindowListener::GotFocus()
		{
		}

		void INativeWindowListener::LostFocus()
		{
		}

		void INativeWindowListener::Activated()
		{
		}

		void INativeWindowListener::Deactivated()
		{
		}

		void INativeWindowListener::Opened()
		{
		}

		void INativeWindowListener::Closing(bool& cancel)
		{
		}

		void INativeWindowListener::Closed()
		{
		}

		void INativeWindowListener::Paint()
		{
		}

		void INativeWindowListener::Destroying()
		{
		}

		void INativeWindowListener::Destroyed()
		{
		}

		void INativeWindowListener::LeftButtonDown(const NativeWindowMouseInfo& info)
		{
		}

		void INativeWindowListener::LeftButtonUp(const NativeWindowMouseInfo& info)
		{
		}

		void INativeWindowListener::LeftButtonDoubleClick(const NativeWindowMouseInfo& info)
		{
		}

		void INativeWindowListener::RightButtonDown(const NativeWindowMouseInfo& info)
		{
		}

		void INativeWindowListener::RightButtonUp(const NativeWindowMouseInfo& info)
		{
		}

		void INativeWindowListener::RightButtonDoubleClick(const NativeWindowMouseInfo& info)
		{
		}

		void INativeWindowListener::MiddleButtonDown(const NativeWindowMouseInfo& info)
		{
		}

		void INativeWindowListener::MiddleButtonUp(const NativeWindowMouseInfo& info)
		{
		}

		void INativeWindowListener::MiddleButtonDoubleClick(const NativeWindowMouseInfo& info)
		{
		}

		void INativeWindowListener::HorizontalWheel(const NativeWindowMouseInfo& info)
		{
		}

		void INativeWindowListener::VerticalWheel(const NativeWindowMouseInfo& info)
		{
		}

		void INativeWindowListener::MouseMoving(const NativeWindowMouseInfo& info)
		{
		}

		void INativeWindowListener::MouseEntered()
		{
		}

		void INativeWindowListener::MouseLeaved()
		{
		}

		void INativeWindowListener::KeyDown(const NativeWindowKeyInfo& info)
		{
		}

		void INativeWindowListener::KeyUp(const NativeWindowKeyInfo& info)
		{
		}

		void INativeWindowListener::SysKeyDown(const NativeWindowKeyInfo& info)
		{
		}

		void INativeWindowListener::SysKeyUp(const NativeWindowKeyInfo& info)
		{
		}

		void INativeWindowListener::Char(const NativeWindowCharInfo& info)
		{
		}

/***********************************************************************
INativeControllerListener
***********************************************************************/

		void INativeControllerListener::LeftButtonDown(Point position)
		{
		}

		void INativeControllerListener::LeftButtonUp(Point position)
		{
		}

		void INativeControllerListener::RightButtonDown(Point position)
		{
		}

		void INativeControllerListener::RightButtonUp(Point position)
		{
		}

		void INativeControllerListener::MouseMoving(Point position)
		{
		}

		void INativeControllerListener::GlobalTimer()
		{
		}

		void INativeControllerListener::ClipboardUpdated()
		{
		}

		void INativeControllerListener::NativeWindowCreated(INativeWindow* window)
		{
		}

		void INativeControllerListener::NativeWindowDestroying(INativeWindow* window)
		{
		}

/***********************************************************************
Native Window Provider
***********************************************************************/

		INativeController* currentController=0;

		INativeController* GetCurrentController()
		{
			return currentController;
		}

		void SetCurrentController(INativeController* controller)
		{
			currentController=controller;
		}
	}
}

/***********************************************************************
.\RESOURCES\GUIDOCUMENT.CPP
***********************************************************************/

namespace vl
{
	namespace presentation
	{
		using namespace collections;
		using namespace parsing::tabling;
		using namespace parsing::xml;
		using namespace regex;

/***********************************************************************
DocumentFontSize
***********************************************************************/

		DocumentFontSize DocumentFontSize::Parse(const WString& value)
		{
			if (value.Length() > 0 && value[value.Length() - 1] == L'x')
			{
				return DocumentFontSize(wtof(value.Left(value.Length() - 1)), true);
			}
			else
			{
				return DocumentFontSize(wtof(value), false);
			}
		}

		WString DocumentFontSize::ToString()const
		{
			return ftow(size) + (relative ? L"x" : L"");
		}

/***********************************************************************
DocumentImageRun
***********************************************************************/

		const wchar_t* DocumentImageRun::RepresentationText=L"[Image]";
		const wchar_t* DocumentEmbeddedObjectRun::RepresentationText=L"[EmbeddedObject]";

/***********************************************************************
ExtractTextVisitor
***********************************************************************/

		namespace document_operation_visitors
		{
			class ExtractTextVisitor : public Object, public DocumentRun::IVisitor
			{
			public:
				stream::TextWriter&				writer;
				bool							skipNonTextContent;

				ExtractTextVisitor(stream::TextWriter& _writer, bool _skipNonTextContent)
					:writer(_writer)
					,skipNonTextContent(_skipNonTextContent)
				{
				}

				void VisitContainer(DocumentContainerRun* run)
				{
					FOREACH(Ptr<DocumentRun>, subRun, run->runs)
					{
						subRun->Accept(this);
					}
				}

				void VisitContent(DocumentContentRun* run)
				{
					writer.WriteString(run->GetRepresentationText());
				}

				void Visit(DocumentTextRun* run)override
				{
					VisitContent(run);
				}

				void Visit(DocumentStylePropertiesRun* run)override
				{
					VisitContainer(run);
				}

				void Visit(DocumentStyleApplicationRun* run)override
				{
					VisitContainer(run);
				}

				void Visit(DocumentHyperlinkRun* run)override
				{
					VisitContainer(run);
				}

				void Visit(DocumentImageRun* run)override
				{
					if(!skipNonTextContent)
					{
						VisitContent(run);
					}
				}

				void Visit(DocumentEmbeddedObjectRun* run)override
				{
					if(!skipNonTextContent)
					{
						VisitContent(run);
					}
				}

				void Visit(DocumentParagraphRun* run)override
				{
					VisitContainer(run);
				}
			};
		}
		using namespace document_operation_visitors;

/***********************************************************************
DocumentParagraphRun
***********************************************************************/

		WString DocumentParagraphRun::GetText(bool skipNonTextContent)
		{
			stream::MemoryStream memoryStream;
			{
				stream::StreamWriter writer(memoryStream);
				GetText(writer, skipNonTextContent);
			}

			memoryStream.SeekFromBegin(0);
			stream::StreamReader reader(memoryStream);
			return reader.ReadToEnd();
		}

		void DocumentParagraphRun::GetText(stream::TextWriter& writer, bool skipNonTextContent)
		{
			ExtractTextVisitor visitor(writer, skipNonTextContent);
			Accept(&visitor);
		}

/***********************************************************************
DocumentModel
***********************************************************************/

		const wchar_t* DocumentModel::DefaultStyleName		= L"#Default";
		const wchar_t* DocumentModel::SelectionStyleName	= L"#Selection";
		const wchar_t* DocumentModel::ContextStyleName		= L"#Context";
		const wchar_t* DocumentModel::NormalLinkStyleName	= L"#NormalLink";
		const wchar_t* DocumentModel::ActiveLinkStyleName	= L"#ActiveLink";

		DocumentModel::DocumentModel()
		{
			{
				FontProperties font=GetCurrentController()->ResourceService()->GetDefaultFont();
				Ptr<DocumentStyleProperties> sp=new DocumentStyleProperties;
				sp->face=font.fontFamily;
				sp->size=DocumentFontSize((double)font.size, false);
				sp->color=Color();
				sp->backgroundColor=Color(0, 0, 0, 0);
				sp->bold=font.bold;
				sp->italic=font.italic;
				sp->underline=font.underline;
				sp->strikeline=font.strikeline;
				sp->antialias=font.antialias;
				sp->verticalAntialias=font.verticalAntialias;

				Ptr<DocumentStyle> style=new DocumentStyle;
				style->styles=sp;
				styles.Add(L"#Default", style);
			}
			{
				Ptr<DocumentStyleProperties> sp=new DocumentStyleProperties;
				sp->color=Color(255, 255, 255);
				sp->backgroundColor=Color(51, 153, 255);

				Ptr<DocumentStyle> style=new DocumentStyle;
				style->styles=sp;
				styles.Add(L"#Selection", style);
			}
			{
				Ptr<DocumentStyleProperties> sp=new DocumentStyleProperties;

				Ptr<DocumentStyle> style=new DocumentStyle;
				style->styles=sp;
				styles.Add(L"#Context", style);
			}
			{
				Ptr<DocumentStyleProperties> sp=new DocumentStyleProperties;
				sp->color=Color(0, 0, 255);
				sp->underline=true;

				Ptr<DocumentStyle> style=new DocumentStyle;
				style->parentStyleName=L"#Context";
				style->styles=sp;
				styles.Add(L"#NormalLink", style);
			}
			{
				Ptr<DocumentStyleProperties> sp=new DocumentStyleProperties;
				sp->color=Color(255, 128, 0);
				sp->underline=true;

				Ptr<DocumentStyle> style=new DocumentStyle;
				style->parentStyleName=L"#Context";
				style->styles=sp;
				styles.Add(L"#ActiveLink", style);
			}
		}

		void DocumentModel::MergeStyle(Ptr<DocumentStyleProperties> style, Ptr<DocumentStyleProperties> parent)
		{
			if(!style->face					&& parent->face)				style->face					=parent->face;
			if(!style->size					&& parent->size)				style->size					=parent->size;
			if(!style->color				&& parent->color)				style->color				=parent->color;
			if(!style->backgroundColor		&& parent->backgroundColor)		style->backgroundColor		=parent->backgroundColor;
			if(!style->bold					&& parent->bold)				style->bold					=parent->bold;
			if(!style->italic				&& parent->italic)				style->italic				=parent->italic;
			if(!style->underline			&& parent->underline)			style->underline			=parent->underline;
			if(!style->strikeline			&& parent->strikeline)			style->strikeline			=parent->strikeline;
			if(!style->antialias			&& parent->antialias)			style->antialias			=parent->antialias;
			if(!style->verticalAntialias	&& parent->verticalAntialias)	style->verticalAntialias	=parent->verticalAntialias;
		}

		void DocumentModel::MergeBaselineStyle(Ptr<DocumentStyleProperties> style, const WString& styleName)
		{
			auto indexDst = styles.Keys().IndexOf(styleName);
			Ptr<DocumentStyleProperties> sp = new DocumentStyleProperties;
			MergeStyle(sp, style);
			if (indexDst != -1)
			{
				MergeStyle(sp, styles.Values()[indexDst]->styles);
			}

			if (indexDst == -1)
			{
				auto style = new DocumentStyle;
				style->styles = sp;
				styles.Add(styleName, style);
			}
			else
			{
				styles.Values()[indexDst]->styles = sp;
			}

			FOREACH(Ptr<DocumentStyle>, style, styles.Values())
			{
				style->resolvedStyles = nullptr;
			}
		}

		void DocumentModel::MergeBaselineStyle(Ptr<DocumentModel> baselineDocument, const WString& styleName)
		{
			auto indexSrc = baselineDocument->styles.Keys().IndexOf(styleName + L"-Override");
			if (indexSrc == -1)
			{
				return;
			}

			auto csp = baselineDocument->styles.Values()[indexSrc]->styles;
			MergeBaselineStyle(csp, styleName);
		}

		void DocumentModel::MergeBaselineStyles(Ptr<DocumentModel> baselineDocument)
		{
			MergeBaselineStyle(baselineDocument, DefaultStyleName);
			MergeBaselineStyle(baselineDocument, SelectionStyleName);
			MergeBaselineStyle(baselineDocument, ContextStyleName);
			MergeBaselineStyle(baselineDocument, NormalLinkStyleName);
			MergeBaselineStyle(baselineDocument, ActiveLinkStyleName);
		}

		void DocumentModel::MergeDefaultFont(const FontProperties& defaultFont)
		{
			Ptr<DocumentStyleProperties> style = new DocumentStyleProperties;

			style->face					=defaultFont.fontFamily;
			style->size					=DocumentFontSize((double)defaultFont.size, false);
			style->bold					=defaultFont.bold;
			style->italic				=defaultFont.italic;
			style->underline			=defaultFont.underline;
			style->strikeline			=defaultFont.strikeline;
			style->antialias			=defaultFont.antialias;
			style->verticalAntialias	=defaultFont.verticalAntialias;

			MergeBaselineStyle(style, DefaultStyleName);
		}

		DocumentModel::ResolvedStyle DocumentModel::GetStyle(Ptr<DocumentStyleProperties> sp, const ResolvedStyle& context)
		{
			FontProperties font;
			font.fontFamily			=sp->face				?sp->face.Value()				:context.style.fontFamily;
			font.bold				=sp->bold				?sp->bold.Value()				:context.style.bold;
			font.italic				=sp->italic				?sp->italic.Value()				:context.style.italic;
			font.underline			=sp->underline			?sp->underline.Value()			:context.style.underline;
			font.strikeline			=sp->strikeline			?sp->strikeline.Value()			:context.style.strikeline;
			font.antialias			=sp->antialias			?sp->antialias.Value()			:context.style.antialias;
			font.verticalAntialias	=sp->verticalAntialias	?sp->verticalAntialias.Value()	:context.style.verticalAntialias;
			Color color				=sp->color				?sp->color.Value()				:context.color;
			Color backgroundColor	=sp->backgroundColor	?sp->backgroundColor.Value()	:context.backgroundColor;

			if (sp->size)
			{
				font.size = (vint)(sp->size.Value().relative ? context.style.size * sp->size.Value().size : sp->size.Value().size);
			}
			else
			{
				font.size = context.style.size;
			}
			return ResolvedStyle(font, color, backgroundColor);
		}

		DocumentModel::ResolvedStyle DocumentModel::GetStyle(const WString& styleName, const ResolvedStyle& context)
		{
			Ptr<DocumentStyle> selectedStyle;
			{
				vint index=styles.Keys().IndexOf(styleName);
				if(index!=-1)
				{
					selectedStyle=styles.Values()[index];
				}
				else
				{
					selectedStyle=styles[L"#Default"];
				}
			}

			if(!selectedStyle->resolvedStyles)
			{
				Ptr<DocumentStyleProperties> sp = new DocumentStyleProperties;
				selectedStyle->resolvedStyles = sp;

				Ptr<DocumentStyle> currentStyle;
				WString currentName = styleName;
				while(true)
				{
					vint index = styles.Keys().IndexOf(currentName);
					if (index == -1) break;
					currentStyle = styles.Values().Get(index);
					currentName = currentStyle->parentStyleName;
					MergeStyle(sp, currentStyle->styles);
				}
			}

			Ptr<DocumentStyleProperties> sp=selectedStyle->resolvedStyles;
			return GetStyle(sp, context);
		}

		WString DocumentModel::GetText(bool skipNonTextContent)
		{
			stream::MemoryStream memoryStream;
			{
				stream::StreamWriter writer(memoryStream);
				GetText(writer, skipNonTextContent);
			}

			memoryStream.SeekFromBegin(0);
			stream::StreamReader reader(memoryStream);
			return reader.ReadToEnd();
		}

		void DocumentModel::GetText(stream::TextWriter& writer, bool skipNonTextContent)
		{
			for(vint i=0;i<paragraphs.Count();i++)
			{
				Ptr<DocumentParagraphRun> paragraph=paragraphs[i];
				paragraph->GetText(writer, skipNonTextContent);
				if(i<paragraphs.Count()-1)
				{
					writer.WriteString(L"\r\n\r\n");
				}
			}
		}
	}
}

/***********************************************************************
.\RESOURCES\GUIDOCUMENT_SAVE.CPP
***********************************************************************/

namespace vl
{
	namespace presentation
	{
		using namespace collections;
		using namespace parsing::xml;

/***********************************************************************
document_operation_visitors::SerializeRunVisitor
***********************************************************************/

		namespace document_operation_visitors
		{
			class SerializeRunVisitor : public Object, public DocumentRun::IVisitor
			{
			protected:
				DocumentModel*				model;
				Ptr<XmlElement>				parent;

			public:
				SerializeRunVisitor(DocumentModel* _model, Ptr<XmlElement> _parent)
					:model(_model)
					, parent(_parent)
				{
				}

				void VisitContainer(Ptr<XmlElement> replacedParent, DocumentContainerRun* run)
				{
					if (replacedParent)
					{
						parent->subNodes.Add(replacedParent);
						Ptr<XmlElement> oldParent = parent;
						parent = replacedParent;
						FOREACH(Ptr<DocumentRun>, subRun, run->runs)
						{
							subRun->Accept(this);
						}
						parent = oldParent;
					}
					else
					{
						FOREACH(Ptr<DocumentRun>, subRun, run->runs)
						{
							subRun->Accept(this);
						}
					}
				}

				void Visit(DocumentTextRun* run)override
				{
					if (run->text != L"")
					{
						auto writer = XmlElementWriter(parent).Element(L"nop");
						auto begin = run->text.Buffer();
						auto reading = begin;
						auto last = reading;
						while (true)
						{
							const wchar_t* tag = nullptr;
							auto c = *reading;
							switch (c)
							{
							case L'\n':
								tag = L"br";
								break;
							case L' ':
								tag = L"sp";
								break;
							case L'\t':
								tag = L"tab";
								break;
							}

							if (tag || c == 0)
							{
								if (reading > last)
								{
									auto end = reading[-1] == L'\r' ? reading - 1 : reading;
									if (end > last)
									{
										writer.Text(run->text.Sub(last - begin, end - last));
									}
									last = reading;
								}
							}

							if (tag)
							{
								writer.Element(tag);
							}
							else if (c == 0)
							{
								break;
							}
							reading++;
						}
					}
				}

				void Visit(DocumentStylePropertiesRun* run)override
				{
					Ptr<DocumentStyleProperties> sp = run->style;
					Ptr<XmlElement> oldParent = parent;
					if (sp->face || sp->size || sp->color)
					{
						Ptr<XmlElement> element = new XmlElement;
						element->name.value = L"font";
						parent->subNodes.Add(element);

						XmlElementWriter writer(element);
						if (sp->face)
						{
							writer.Attribute(L"face", sp->face.Value());
						}
						if (sp->size)
						{
							writer.Attribute(L"size", sp->size.Value().ToString());
						}
						if (sp->color)
						{
							writer.Attribute(L"color", sp->color.Value().ToString());
						}
						if (sp->backgroundColor)
						{
							writer.Attribute(L"bkcolor", sp->backgroundColor.Value().ToString());
						}
						parent = element;
					}
					if (sp->bold)
					{
						Ptr<XmlElement> element = new XmlElement;
						element->name.value = sp->bold.Value() ? L"b" : L"b-";
						parent->subNodes.Add(element);
						parent = element;
					}
					if (sp->italic)
					{
						Ptr<XmlElement> element = new XmlElement;
						element->name.value = sp->italic.Value() ? L"i" : L"i-";
						parent->subNodes.Add(element);
						parent = element;
					}
					if (sp->underline)
					{
						Ptr<XmlElement> element = new XmlElement;
						element->name.value = sp->underline.Value() ? L"u" : L"u-";
						parent->subNodes.Add(element);
						parent = element;
					}
					if (sp->strikeline)
					{
						Ptr<XmlElement> element = new XmlElement;
						element->name.value = sp->strikeline.Value() ? L"s" : L"s-";
						parent->subNodes.Add(element);
						parent = element;
					}
					if (sp->antialias || sp->verticalAntialias)
					{
						bool ha = sp->antialias ? sp->antialias.Value() : true;
						bool va = sp->verticalAntialias ? sp->verticalAntialias.Value() : false;
						if (!ha)
						{
							Ptr<XmlElement> element = new XmlElement;
							element->name.value = L"ha";
							parent->subNodes.Add(element);
							parent = element;
						}
						else if (!va)
						{
							Ptr<XmlElement> element = new XmlElement;
							element->name.value = L"va";
							parent->subNodes.Add(element);
							parent = element;
						}
						else
						{
							Ptr<XmlElement> element = new XmlElement;
							element->name.value = L"na";
							parent->subNodes.Add(element);
							parent = element;
						}
					}
					VisitContainer(0, run);
					parent = oldParent;
				}

				void Visit(DocumentStyleApplicationRun* run)override
				{
					Ptr<XmlElement> element = new XmlElement;
					element->name.value = L"div";
					XmlElementWriter(element).Attribute(L"style", run->styleName);
					VisitContainer(element, run);
				}

				void Visit(DocumentHyperlinkRun* run)override
				{
					Ptr<XmlElement> element = new XmlElement;
					element->name.value = L"a";
					XmlElementWriter writer(element);
					if (run->normalStyleName != L"#NormalLink")
					{
						writer.Attribute(L"normal", run->normalStyleName);
					}
					if (run->activeStyleName != L"#ActiveLink")
					{
						writer.Attribute(L"active", run->activeStyleName);
					}
					if (run->reference != L"")
					{
						writer.Attribute(L"href", run->reference);
					}
					VisitContainer(element, run);
				}

				void Visit(DocumentImageRun* run)override
				{
					XmlElementWriter writer(parent);
					writer
						.Element(L"img")
						.Attribute(L"width", itow(run->size.x))
						.Attribute(L"height", itow(run->size.y))
						.Attribute(L"baseline", itow(run->baseline))
						.Attribute(L"frameIndex", itow(run->frameIndex))
						.Attribute(L"source", run->source)
						;
				}

				void Visit(DocumentEmbeddedObjectRun* run)override
				{
					XmlElementWriter writer(parent);
					writer
						.Element(L"object")
						.Attribute(L"name", run->name)
						;
				}

				void Visit(DocumentParagraphRun* run)override
				{
					Ptr<XmlElement> element = new XmlElement;
					element->name.value = L"p";

					XmlElementWriter writer(element);
					if (run->alignment)
					{
						switch (run->alignment.Value())
						{
						case Alignment::Left:
							writer.Attribute(L"align", L"Left");
							break;
						case Alignment::Center:
							writer.Attribute(L"align", L"Center");
							break;
						case Alignment::Right:
							writer.Attribute(L"align", L"Right");
							break;
						}
					}
					VisitContainer(element, run);
				}
			};
		}
		using namespace document_operation_visitors;

/***********************************************************************
DocumentModel
***********************************************************************/

		Ptr<parsing::xml::XmlDocument> DocumentModel::SaveToXml()
		{
			Ptr<XmlDocument> xml=new XmlDocument;
			Ptr<XmlElement> doc=new XmlElement;
			doc->name.value=L"Doc";
			xml->rootElement=doc;
			{
				Ptr<XmlElement> content=new XmlElement;
				content->name.value=L"Content";
				doc->subNodes.Add(content);
				
				FOREACH(Ptr<DocumentParagraphRun>, p, paragraphs)
				{
					SerializeRunVisitor visitor(this, content);
					p->Accept(&visitor);
				}
			}
			{
				Ptr<XmlElement> stylesElement=new XmlElement;
				stylesElement->name.value=L"Styles";
				doc->subNodes.Add(stylesElement);

				for(vint i=0;i<styles.Count();i++)
				{
					WString name=styles.Keys()[i];
					if (name.Length()>0 && name[0] == L'#' && (name.Length() <= 9 || name.Right(9) != L"-Override")) continue;

					Ptr<DocumentStyle> style=styles.Values().Get(i);
					Ptr<DocumentStyleProperties> sp=style->styles;
					Ptr<XmlElement> styleElement=new XmlElement;
					styleElement->name.value=L"Style";
					stylesElement->subNodes.Add(styleElement);

					XmlElementWriter(styleElement).Attribute(L"name", name);
					if(style->parentStyleName!=L"")
					{
						XmlElementWriter(styleElement).Attribute(L"parent", style->parentStyleName);
					}

					if(sp->face)				XmlElementWriter(styleElement).Element(L"face").Text(		sp->face.Value()						);
					if(sp->size)				XmlElementWriter(styleElement).Element(L"size").Text(		sp->size.Value().ToString()				);
					if(sp->color)				XmlElementWriter(styleElement).Element(L"color").Text(		sp->color.Value().ToString()			);
					if(sp->backgroundColor)		XmlElementWriter(styleElement).Element(L"bkcolor").Text(	sp->backgroundColor.Value().ToString()	);
					if(sp->bold)				XmlElementWriter(styleElement).Element(L"b").Text(			sp->bold.Value()?L"true":L"false"		);
					if(sp->italic)				XmlElementWriter(styleElement).Element(L"i").Text(			sp->italic.Value()?L"true":L"false"		);
					if(sp->underline)			XmlElementWriter(styleElement).Element(L"u").Text(			sp->underline.Value()?L"true":L"false"	);
					if(sp->strikeline)			XmlElementWriter(styleElement).Element(L"s").Text(			sp->strikeline.Value()?L"true":L"false"	);
					if(sp->antialias && sp->verticalAntialias)
					{
						bool h=sp->antialias;
						bool v=sp->verticalAntialias;
						if(!h)
						{
							XmlElementWriter(styleElement).Element(L"antialias").Text(L"no");
						}
						else if(!v)
						{
							XmlElementWriter(styleElement).Element(L"antialias").Text(L"horizontal");
						}
						else
						{
							XmlElementWriter(styleElement).Element(L"antialias").Text(L"vertical");
						}
					}
				}
			}
			return xml;
		}
	}
}

/***********************************************************************
.\GRAPHICSCOMPOSITION\GUIGRAPHICSAXIS.CPP
***********************************************************************/

namespace vl
{
	namespace presentation
	{
		namespace compositions
		{

/***********************************************************************
GuiDefaultAxis
***********************************************************************/

			GuiDefaultAxis::GuiDefaultAxis()
			{
			}

			GuiDefaultAxis::~GuiDefaultAxis()
			{
			}

			Size GuiDefaultAxis::RealSizeToVirtualSize(Size size)
			{
				return size;
			}

			Size GuiDefaultAxis::VirtualSizeToRealSize(Size size)
			{
				return size;
			}

			Point GuiDefaultAxis::RealPointToVirtualPoint(Size realFullSize, Point point)
			{
				return point;
			}

			Point GuiDefaultAxis::VirtualPointToRealPoint(Size realFullSize, Point point)
			{
				return point;
			}

			Rect GuiDefaultAxis::RealRectToVirtualRect(Size realFullSize, Rect rect)
			{
				return rect;
			}

			Rect GuiDefaultAxis::VirtualRectToRealRect(Size realFullSize, Rect rect)
			{
				return rect;
			}

			Margin GuiDefaultAxis::RealMarginToVirtualMargin(Margin margin)
			{
				return margin;
			}

			Margin GuiDefaultAxis::VirtualMarginToRealMargin(Margin margin)
			{
				return margin;
			}

			KeyDirection GuiDefaultAxis::RealKeyDirectionToVirtualKeyDirection(KeyDirection key)
			{
				return key;
			}

/***********************************************************************
GuiAxis
***********************************************************************/

			GuiAxis::GuiAxis(AxisDirection _axisDirection)
				:axisDirection(_axisDirection)
			{
			}

			GuiAxis::~GuiAxis()
			{
			}

			AxisDirection GuiAxis::GetDirection()
			{
				return axisDirection;
			}

			Size GuiAxis::RealSizeToVirtualSize(Size size)
			{
				switch(axisDirection)
				{
				case AxisDirection::LeftDown:
				case AxisDirection::RightDown:
				case AxisDirection::LeftUp:
				case AxisDirection::RightUp:
					return Size(size.x, size.y);
				case AxisDirection::DownLeft:
				case AxisDirection::DownRight:
				case AxisDirection::UpLeft:
				case AxisDirection::UpRight:
					return Size(size.y, size.x);
				}
				return size;
			}

			Size GuiAxis::VirtualSizeToRealSize(Size size)
			{
				return RealSizeToVirtualSize(size);
			}

			Point GuiAxis::RealPointToVirtualPoint(Size realFullSize, Point point)
			{
				Rect rect(point, Size(0, 0));
				return RealRectToVirtualRect(realFullSize, rect).LeftTop();
			}

			Point GuiAxis::VirtualPointToRealPoint(Size realFullSize, Point point)
			{
				Rect rect(point, Size(0, 0));
				return VirtualRectToRealRect(realFullSize, rect).LeftTop();
			}

			Rect GuiAxis::RealRectToVirtualRect(Size realFullSize, Rect rect)
			{
				vint x1=rect.x1;
				vint x2=realFullSize.x-rect.x2;
				vint y1=rect.y1;
				vint y2=realFullSize.y-rect.y2;
				vint w=rect.Width();
				vint h=rect.Height();
				switch(axisDirection)
				{
				case AxisDirection::LeftDown:
					return Rect(Point(x2, y1), Size(w, h));
				case AxisDirection::RightDown:
					return Rect(Point(x1, y1), Size(w, h));
				case AxisDirection::LeftUp:
					return Rect(Point(x2, y2), Size(w, h));
				case AxisDirection::RightUp:
					return Rect(Point(x1, y2), Size(w, h));
				case AxisDirection::DownLeft:
					return Rect(Point(y1, x2), Size(h, w));
				case AxisDirection::DownRight:
					return Rect(Point(y1, x1), Size(h, w));
				case AxisDirection::UpLeft:
					return Rect(Point(y2, x2), Size(h, w));
				case AxisDirection::UpRight:
					return Rect(Point(y2, x1), Size(h, w));
				}
				return rect;
			}

			Rect GuiAxis::VirtualRectToRealRect(Size realFullSize, Rect rect)
			{
				realFullSize=RealSizeToVirtualSize(realFullSize);
				vint x1=rect.x1;
				vint x2=realFullSize.x-rect.x2;
				vint y1=rect.y1;
				vint y2=realFullSize.y-rect.y2;
				vint w=rect.Width();
				vint h=rect.Height();
				switch(axisDirection)
				{
				case AxisDirection::LeftDown:
					return Rect(Point(x2, y1), Size(w, h));
				case AxisDirection::RightDown:
					return Rect(Point(x1, y1), Size(w, h));
				case AxisDirection::LeftUp:
					return Rect(Point(x2, y2), Size(w, h));
				case AxisDirection::RightUp:
					return Rect(Point(x1, y2), Size(w, h));
				case AxisDirection::DownLeft:
					return Rect(Point(y2, x1), Size(h, w));
				case AxisDirection::DownRight:
					return Rect(Point(y1, x1), Size(h, w));
				case AxisDirection::UpLeft:
					return Rect(Point(y2, x2), Size(h, w));
				case AxisDirection::UpRight:
					return Rect(Point(y1, x2), Size(h, w));
				}
				return rect;
			}

			Margin GuiAxis::RealMarginToVirtualMargin(Margin margin)
			{
				vint x1=margin.left;
				vint x2=margin.right;
				vint y1=margin.top;
				vint y2=margin.bottom;
				switch(axisDirection)
				{
				case AxisDirection::LeftDown:
					return Margin(x2, y1, x1, y2);
				case AxisDirection::RightDown:
					return Margin(x1, y1, x2, y2);
				case AxisDirection::LeftUp:
					return Margin(x2, y2, x1, y1);
				case AxisDirection::RightUp:
					return Margin(x1, y2, x2, y1);
				case AxisDirection::DownLeft:
					return Margin(y1, x2, y2, x1);
				case AxisDirection::DownRight:
					return Margin(y1, x1, y2, x2);
				case AxisDirection::UpLeft:
					return Margin(y2, x2, y1, x1);
				case AxisDirection::UpRight:
					return Margin(y2, x1, y1, x2);
				}
				return margin;
			}

			Margin GuiAxis::VirtualMarginToRealMargin(Margin margin)
			{
				vint x1=margin.left;
				vint x2=margin.right;
				vint y1=margin.top;
				vint y2=margin.bottom;
				switch(axisDirection)
				{
				case AxisDirection::LeftDown:
					return Margin(x2, y1, x1, y2);
				case AxisDirection::RightDown:
					return Margin(x1, y1, x2, y2);
				case AxisDirection::LeftUp:
					return Margin(x2, y2, x1, y1);
				case AxisDirection::RightUp:
					return Margin(x1, y2, x2, y1);
				case AxisDirection::DownLeft:
					return Margin(y2, x1, y1, x2);
				case AxisDirection::DownRight:
					return Margin(y1, x1, y2, x2);
				case AxisDirection::UpLeft:
					return Margin(y2, x2, y1, x1);
				case AxisDirection::UpRight:
					return Margin(y1, x2, y2, x1);
				default:;
				}
				return margin;
			}

			KeyDirection GuiAxis::RealKeyDirectionToVirtualKeyDirection(KeyDirection key)
			{
				bool pageKey=false;
				switch(key)
				{
				case KeyDirection::PageUp:
					pageKey=true;
					key=KeyDirection::Up;
					break;
				case KeyDirection::PageDown:
					pageKey=true;
					key=KeyDirection::Down;
					break;
				case KeyDirection::PageLeft:
					pageKey=true;
					key=KeyDirection::Left;
					break;
				case KeyDirection::PageRight:
					pageKey=true;
					key=KeyDirection::Right;
					break;
				default:;
				}

				switch(key)
				{
				case KeyDirection::Up:
					switch(axisDirection)
					{
					case AxisDirection::LeftDown:	key=KeyDirection::Up;		break;
					case AxisDirection::RightDown:	key=KeyDirection::Up;		break;
					case AxisDirection::LeftUp:		key=KeyDirection::Down;	break;
					case AxisDirection::RightUp:	key=KeyDirection::Down;	break;
					case AxisDirection::DownLeft:	key=KeyDirection::Left;	break;
					case AxisDirection::DownRight:	key=KeyDirection::Left;	break;
					case AxisDirection::UpLeft:		key=KeyDirection::Right;	break;
					case AxisDirection::UpRight:	key=KeyDirection::Right;	break;
					}
					break;
				case KeyDirection::Down:
					switch(axisDirection)
					{
					case AxisDirection::LeftDown:	key=KeyDirection::Down;	break;
					case AxisDirection::RightDown:	key=KeyDirection::Down;	break;
					case AxisDirection::LeftUp:		key=KeyDirection::Up;		break;
					case AxisDirection::RightUp:	key=KeyDirection::Up;		break;
					case AxisDirection::DownLeft:	key=KeyDirection::Right;	break;
					case AxisDirection::DownRight:	key=KeyDirection::Right;	break;
					case AxisDirection::UpLeft:		key=KeyDirection::Left;	break;
					case AxisDirection::UpRight:	key=KeyDirection::Left;	break;
					}
					break;
				case KeyDirection::Left:
					switch(axisDirection)
					{
					case AxisDirection::LeftDown:	key=KeyDirection::Right;	break;
					case AxisDirection::RightDown:	key=KeyDirection::Left;	break;
					case AxisDirection::LeftUp:		key=KeyDirection::Right;	break;
					case AxisDirection::RightUp:	key=KeyDirection::Left;	break;
					case AxisDirection::DownLeft:	key=KeyDirection::Down;	break;
					case AxisDirection::DownRight:	key=KeyDirection::Up;		break;
					case AxisDirection::UpLeft:		key=KeyDirection::Down;	break;
					case AxisDirection::UpRight:	key=KeyDirection::Up;		break;
					}
					break;
				case KeyDirection::Right:
					switch(axisDirection)
					{
					case AxisDirection::LeftDown:	key=KeyDirection::Left;	break;
					case AxisDirection::RightDown:	key=KeyDirection::Right;	break;
					case AxisDirection::LeftUp:		key=KeyDirection::Left;	break;
					case AxisDirection::RightUp:	key=KeyDirection::Right;	break;
					case AxisDirection::DownLeft:	key=KeyDirection::Up;		break;
					case AxisDirection::DownRight:	key=KeyDirection::Down;	break;
					case AxisDirection::UpLeft:		key=KeyDirection::Up;		break;
					case AxisDirection::UpRight:	key=KeyDirection::Down;	break;
					}
					break;
				case KeyDirection::Home:
					switch(axisDirection)
					{
					case AxisDirection::LeftDown:	key=KeyDirection::Home;	break;
					case AxisDirection::RightDown:	key=KeyDirection::Home;	break;
					case AxisDirection::LeftUp:		key=KeyDirection::End;	break;
					case AxisDirection::RightUp:	key=KeyDirection::End;	break;
					case AxisDirection::DownLeft:	key=KeyDirection::Home;	break;
					case AxisDirection::DownRight:	key=KeyDirection::Home;	break;
					case AxisDirection::UpLeft:		key=KeyDirection::End;	break;
					case AxisDirection::UpRight:	key=KeyDirection::End;	break;
					}
					break;
				case KeyDirection::End:
					switch(axisDirection)
					{
					case AxisDirection::LeftDown:	key=KeyDirection::End;	break;
					case AxisDirection::RightDown:	key=KeyDirection::End;	break;
					case AxisDirection::LeftUp:		key=KeyDirection::Home;	break;
					case AxisDirection::RightUp:	key=KeyDirection::Home;	break;
					case AxisDirection::DownLeft:	key=KeyDirection::End;	break;
					case AxisDirection::DownRight:	key=KeyDirection::End;	break;
					case AxisDirection::UpLeft:		key=KeyDirection::Home;	break;
					case AxisDirection::UpRight:	key=KeyDirection::Home;	break;
					}
					break;
				default:;
				}

				if(pageKey)
				{
					switch(key)
					{
					case KeyDirection::Up:
						key=KeyDirection::PageUp;
						break;
					case KeyDirection::Down:
						key=KeyDirection::PageDown;
						break;
					case KeyDirection::Left:
						key=KeyDirection::PageLeft;
						break;
					case KeyDirection::Right:
						key=KeyDirection::PageRight;
						break;
					default:;
					}
				}
				return key;
			}
		}
	}
}

/***********************************************************************
.\GRAPHICSCOMPOSITION\GUIGRAPHICSBASICCOMPOSITION.CPP
***********************************************************************/

namespace vl
{
	namespace presentation
	{
		namespace compositions
		{
			using namespace collections;
			using namespace controls;
			using namespace elements;

/***********************************************************************
GuiWindowComposition
***********************************************************************/

			GuiWindowComposition::GuiWindowComposition()
			{
			}

			GuiWindowComposition::~GuiWindowComposition()
			{
			}

			Rect GuiWindowComposition::GetBounds()
			{
				Rect bounds;
				if (relatedHostRecord)
				{
					if (auto window = relatedHostRecord->host->GetNativeWindow())
					{
						bounds = Rect(Point(0, 0), window->GetClientSize());
					}
				}
				UpdatePreviousBounds(bounds);
				return bounds;
			}

			void GuiWindowComposition::SetMargin(Margin value)
			{
			}

/***********************************************************************
GuiBoundsComposition
***********************************************************************/

			GuiBoundsComposition::GuiBoundsComposition()
			{
			}

			GuiBoundsComposition::~GuiBoundsComposition()
			{
			}

			bool GuiBoundsComposition::GetSizeAffectParent()
			{
				return sizeAffectParent;
			}

			void GuiBoundsComposition::SetSizeAffectParent(bool value)
			{
				sizeAffectParent = value;
			}

			bool GuiBoundsComposition::IsSizeAffectParent()
			{
				return sizeAffectParent;
			}

			Rect GuiBoundsComposition::GetPreferredBounds()
			{
				Rect result = GetBoundsInternal(compositionBounds);
				if (GetParent() && IsAlignedToParent())
				{
					if (alignmentToParent.left >= 0)
					{
						vint offset = alignmentToParent.left - result.x1;
						result.x1 += offset;
						result.x2 += offset;
					}
					if (alignmentToParent.top >= 0)
					{
						vint offset = alignmentToParent.top - result.y1;
						result.y1 += offset;
						result.y2 += offset;
					}
					if (alignmentToParent.right >= 0)
					{
						result.x2 += alignmentToParent.right;
					}
					if (alignmentToParent.bottom >= 0)
					{
						result.y2 += alignmentToParent.bottom;
					}
				}
				return result;
			}

			Rect GuiBoundsComposition::GetBounds()
			{
				Rect result = GetPreferredBounds();
				if (GetParent() && IsAlignedToParent())
				{
					Size clientSize = GetParent()->GetClientArea().GetSize();
					if (alignmentToParent.left >= 0 && alignmentToParent.right >= 0)
					{
						result.x1 = alignmentToParent.left;
						result.x2 = clientSize.x - alignmentToParent.right;
					}
					else if (alignmentToParent.left >= 0)
					{
						vint width = result.Width();
						result.x1 = alignmentToParent.left;
						result.x2 = result.x1 + width;
					}
					else if (alignmentToParent.right >= 0)
					{
						vint width = result.Width();
						result.x2 = clientSize.x - alignmentToParent.right;
						result.x1 = result.x2 - width;
					}

					if (alignmentToParent.top >= 0 && alignmentToParent.bottom >= 0)
					{
						result.y1 = alignmentToParent.top;
						result.y2 = clientSize.y - alignmentToParent.bottom;
					}
					else if (alignmentToParent.top >= 0)
					{
						vint height = result.Height();
						result.y1 = alignmentToParent.top;
						result.y2 = result.y1 + height;
					}
					else if (alignmentToParent.bottom >= 0)
					{
						vint height = result.Height();
						result.y2 = clientSize.y - alignmentToParent.bottom;
						result.y1 = result.y2 - height;
					}
				}
				UpdatePreviousBounds(result);
				return result;
			}

			void GuiBoundsComposition::SetBounds(Rect value)
			{
				compositionBounds = value;
				InvokeOnCompositionStateChanged();
			}

			Margin GuiBoundsComposition::GetAlignmentToParent()
			{
				return alignmentToParent;
			}

			void GuiBoundsComposition::SetAlignmentToParent(Margin value)
			{
				alignmentToParent = value;
				InvokeOnCompositionStateChanged();
			}

			bool GuiBoundsComposition::IsAlignedToParent()
			{
				return alignmentToParent != Margin(-1, -1, -1, -1);
			}
		}
	}
}

/***********************************************************************
.\GRAPHICSCOMPOSITION\GUIGRAPHICSFLOWCOMPOSITION.CPP
***********************************************************************/

namespace vl
{
	namespace presentation
	{
		namespace compositions
		{
			using namespace collections;

/***********************************************************************
GuiFlowComposition
***********************************************************************/

			void GuiFlowComposition::UpdateFlowItemBounds(bool forceUpdate)
			{
				if (forceUpdate || needUpdate)
				{
					needUpdate = false;
					InvokeOnCompositionStateChanged();

					auto clientMargin = axis->RealMarginToVirtualMargin(extraMargin);
					if (clientMargin.left < 0) clientMargin.left = 0;
					if (clientMargin.top < 0) clientMargin.top = 0;
					if (clientMargin.right < 0) clientMargin.right = 0;
					if (clientMargin.bottom < 0) clientMargin.bottom = 0;

					auto realFullSize = previousBounds.GetSize();
					auto clientSize = axis->RealSizeToVirtualSize(realFullSize);
					clientSize.x -= (clientMargin.left + clientMargin.right);
					clientSize.y -= (clientMargin.top + clientMargin.bottom);

					flowItemBounds.Resize(flowItems.Count());
					for (vint i = 0; i < flowItems.Count(); i++)
					{
						flowItemBounds[i] = Rect(Point(0, 0), flowItems[i]->GetMinSize());
					}

					vint currentIndex = 0;
					vint rowTop = 0;

					while (currentIndex < flowItems.Count())
					{
						auto itemSize = axis->RealSizeToVirtualSize(flowItemBounds[currentIndex].GetSize());
						vint rowWidth = itemSize.x;
						vint rowHeight = itemSize.y;
						vint rowItemCount = 1;

						for (vint i = currentIndex + 1; i < flowItems.Count(); i++)
						{
							itemSize = axis->RealSizeToVirtualSize(flowItemBounds[i].GetSize());
							vint itemWidth = itemSize.x + columnPadding;
							if (rowWidth + itemWidth > clientSize.x)
							{
								break;
							}
							rowWidth += itemWidth;
							if (rowHeight < itemSize.y)
							{
								rowHeight = itemSize.y;
							}
							rowItemCount++;
						}

						vint baseLine = 0;
						Array<vint> itemBaseLines(rowItemCount);
						for (vint i = 0; i < rowItemCount; i++)
						{
							vint index = currentIndex + i;
							vint itemBaseLine = 0;
							itemSize = axis->RealSizeToVirtualSize(flowItemBounds[index].GetSize());

							auto option = flowItems[index]->GetFlowOption();
							switch (option.baseline)
							{
							case GuiFlowOption::FromTop:
								itemBaseLine = option.distance;
								break;
							case GuiFlowOption::FromBottom:
								itemBaseLine = itemSize.y - option.distance;
								break;
							case GuiFlowOption::Percentage:
								itemBaseLine = (vint)(itemSize.y*option.percentage);
								break;
							}

							itemBaseLines[i] = itemBaseLine;
							if (baseLine < itemBaseLine)
							{
								baseLine = itemBaseLine;
							}
						}

						vint rowUsedWidth = 0;
						for (vint i = 0; i < rowItemCount; i++)
						{
							vint index = currentIndex + i;
							itemSize = axis->RealSizeToVirtualSize(flowItemBounds[index].GetSize());

							vint itemLeft = 0;
							vint itemTop = rowTop + baseLine - itemBaseLines[i];

							switch (alignment)
							{
							case FlowAlignment::Left:
								itemLeft = rowUsedWidth + i * columnPadding;
								break;
							case FlowAlignment::Center:
								itemLeft = rowUsedWidth + i * columnPadding + (clientSize.x - rowWidth) / 2;
								break;
							case FlowAlignment::Extend:
								if (i == 0)
								{
									itemLeft = rowUsedWidth;
								}
								else
								{
									itemLeft = rowUsedWidth + (vint)((double)(clientSize.x - rowWidth) * i / (rowItemCount - 1)) + i * columnPadding;
								}
								break;
							}

							flowItemBounds[index] = axis->VirtualRectToRealRect(
								realFullSize,
								Rect(
									Point(
										itemLeft + clientMargin.left,
										itemTop + clientMargin.top
									),
									itemSize
								)
							);
							rowUsedWidth += itemSize.x;
						}

						rowTop += rowHeight + rowPadding;
						currentIndex += rowItemCount;
					}

					minHeight = rowTop == 0 ? 0 : rowTop - rowPadding;
				}
			}

			void GuiFlowComposition::OnBoundsChanged(GuiGraphicsComposition* sender, GuiEventArgs& arguments)
			{
				UpdateFlowItemBounds(true);
			}

			void GuiFlowComposition::OnChildInserted(GuiGraphicsComposition* child)
			{
				GuiBoundsComposition::OnChildInserted(child);
				auto item = dynamic_cast<GuiFlowItemComposition*>(child);
				if (item && !flowItems.Contains(item))
				{
					flowItems.Add(item);
					needUpdate = true;
				}
			}

			void GuiFlowComposition::OnChildRemoved(GuiGraphicsComposition* child)
			{
				GuiBoundsComposition::OnChildRemoved(child);
				auto item = dynamic_cast<GuiFlowItemComposition*>(child);
				if (item)
				{
					flowItems.Remove(item);
					needUpdate = true;
				}
			}

			GuiFlowComposition::GuiFlowComposition()
				:axis(new GuiDefaultAxis)
			{
				BoundsChanged.AttachMethod(this, &GuiFlowComposition::OnBoundsChanged);
			}

			GuiFlowComposition::~GuiFlowComposition()
			{
			}

			const GuiFlowComposition::ItemCompositionList& GuiFlowComposition::GetFlowItems()
			{
				return flowItems;
			}

			bool GuiFlowComposition::InsertFlowItem(vint index, GuiFlowItemComposition* item)
			{
				index = flowItems.Insert(index, item);
				if (!AddChild(item))
				{
					flowItems.RemoveAt(index);
					return false;
				}
				else
				{
					needUpdate = true;
					return true;
				}
			}


			Margin GuiFlowComposition::GetExtraMargin()
			{
				return extraMargin;
			}

			void GuiFlowComposition::SetExtraMargin(Margin value)
			{
				extraMargin = value;
				needUpdate = true;
				InvokeOnCompositionStateChanged();
			}

			vint GuiFlowComposition::GetRowPadding()
			{
				return rowPadding;
			}

			void GuiFlowComposition::SetRowPadding(vint value)
			{
				rowPadding = value;
				needUpdate = true;
				InvokeOnCompositionStateChanged();
			}

			vint GuiFlowComposition::GetColumnPadding()
			{
				return columnPadding;
			}

			void GuiFlowComposition::SetColumnPadding(vint value)
			{
				columnPadding = value;
				needUpdate = true;
				InvokeOnCompositionStateChanged();
			}

			Ptr<IGuiAxis> GuiFlowComposition::GetAxis()
			{
				return axis;
			}

			void GuiFlowComposition::SetAxis(Ptr<IGuiAxis> value)
			{
				if (value)
				{
					axis = value;
					needUpdate = true;
					InvokeOnCompositionStateChanged();
				}
			}

			FlowAlignment GuiFlowComposition::GetAlignment()
			{
				return alignment;
			}

			void GuiFlowComposition::SetAlignment(FlowAlignment value)
			{
				alignment = value;
				needUpdate = true;
				InvokeOnCompositionStateChanged();
			}

			void GuiFlowComposition::ForceCalculateSizeImmediately()
			{
				GuiBoundsComposition::ForceCalculateSizeImmediately();
				UpdateFlowItemBounds(true);
			}
			
			Size GuiFlowComposition::GetMinPreferredClientSize()
			{
				Size minSize = GuiBoundsComposition::GetMinPreferredClientSize();
				if (GetMinSizeLimitation() == GuiGraphicsComposition::LimitToElementAndChildren)
				{
					auto clientSize = axis->VirtualSizeToRealSize(Size(0, minHeight));
					if (minSize.x < clientSize.x) minSize.x = clientSize.x;
					if (minSize.y < clientSize.y) minSize.y = clientSize.y;
				}

				vint x = 0;
				vint y = 0;
				if (extraMargin.left > 0) x += extraMargin.left;
				if (extraMargin.right > 0) x += extraMargin.right;
				if (extraMargin.top > 0) y += extraMargin.top;
				if (extraMargin.bottom > 0) y += extraMargin.bottom;
				return minSize + Size(x, y);
			}

			Rect GuiFlowComposition::GetBounds()
			{
				if (!needUpdate)
				{
					for (vint i = 0; i < flowItems.Count(); i++)
					{
						if (flowItemBounds[i].GetSize() != flowItems[i]->GetMinSize())
						{
							needUpdate = true;
							break;
						}
					}
				}

				if (needUpdate)
				{
					UpdateFlowItemBounds(true);
				}

				bounds = GuiBoundsComposition::GetBounds();
				return bounds;
			}

/***********************************************************************
GuiFlowItemComposition
***********************************************************************/

			void GuiFlowItemComposition::OnParentChanged(GuiGraphicsComposition* oldParent, GuiGraphicsComposition* newParent)
			{
				GuiGraphicsSite::OnParentChanged(oldParent, newParent);
				flowParent = newParent == 0 ? 0 : dynamic_cast<GuiFlowComposition*>(newParent);
			}

			Size GuiFlowItemComposition::GetMinSize()
			{
				return GetBoundsInternal(bounds).GetSize();
			}

			GuiFlowItemComposition::GuiFlowItemComposition()
			{
				SetMinSizeLimitation(GuiGraphicsComposition::LimitToElementAndChildren);
			}

			GuiFlowItemComposition::~GuiFlowItemComposition()
			{
			}
			
			bool GuiFlowItemComposition::IsSizeAffectParent()
			{
				return false;
			}

			Rect GuiFlowItemComposition::GetBounds()
			{
				Rect result = bounds;
				if(flowParent)
				{
					flowParent->UpdateFlowItemBounds(false);
					vint index = flowParent->flowItems.IndexOf(this);
					if (index != -1)
					{
						result = flowParent->flowItemBounds[index];
					}

					result = Rect(
						result.Left() - extraMargin.left,
						result.Top() - extraMargin.top,
						result.Right() + extraMargin.right,
						result.Bottom() + extraMargin.bottom
						);
				}
				UpdatePreviousBounds(result);
				return result;
			}

			void GuiFlowItemComposition::SetBounds(Rect value)
			{
				bounds = value;
				InvokeOnCompositionStateChanged();
			}

			Margin GuiFlowItemComposition::GetExtraMargin()
			{
				return extraMargin;
			}

			void GuiFlowItemComposition::SetExtraMargin(Margin value)
			{
				extraMargin = value;
				InvokeOnCompositionStateChanged();
			}

			GuiFlowOption GuiFlowItemComposition::GetFlowOption()
			{
				return option;
			}

			void GuiFlowItemComposition::SetFlowOption(GuiFlowOption value)
			{
				option = value;
				if (flowParent)
				{
					flowParent->needUpdate = true;
					InvokeOnCompositionStateChanged();
				}
			}
		}
	}
}

/***********************************************************************
.\CONTROLS\GUILABELCONTROLS.CPP
***********************************************************************/

namespace vl
{
	namespace presentation
	{
		namespace controls
		{
			using namespace elements;
			using namespace compositions;
			using namespace collections;
			using namespace reflection::description;

/***********************************************************************
GuiLabel
***********************************************************************/

			void GuiLabel::BeforeControlTemplateUninstalled_()
			{
				auto ct = GetControlTemplateObject(false);
				if (!ct) return;

				textColorConsisted = (textColor == ct->GetDefaultTextColor());
			}

			void GuiLabel::AfterControlTemplateInstalled_(bool initialize)
			{
				auto ct = GetControlTemplateObject(true);
				if (initialize || textColorConsisted)
				{
					SetTextColor(ct->GetDefaultTextColor());
				}
				else
				{
					ct->SetTextColor(textColor);
				}
			}

			GuiLabel::GuiLabel(theme::ThemeName themeName)
				:GuiControl(themeName)
			{
			}

			GuiLabel::~GuiLabel()
			{
			}

			Color GuiLabel::GetTextColor()
			{
				return textColor;
			}

			void GuiLabel::SetTextColor(Color value)
			{
				if (textColor != value)
				{
					textColor = value;
					GetControlTemplateObject(true)->SetTextColor(textColor);
				}
			}
		}
	}
}

/***********************************************************************
.\CONTROLS\GUIWINDOWCONTROLS.CPP
***********************************************************************/

namespace vl
{
	namespace presentation
	{
		namespace controls
		{
			using namespace elements;
			using namespace compositions;
			using namespace collections;
			using namespace reflection::description;

/***********************************************************************
GuiControlHost
***********************************************************************/

			void GuiControlHost::OnNativeWindowChanged()
			{
			}

			void GuiControlHost::OnVisualStatusChanged()
			{
			}

			controls::GuiControlHost* GuiControlHost::GetControlHostForInstance()
			{
				return this;
			}

			GuiControl* GuiControlHost::GetTooltipOwner(Point location)
			{
				GuiGraphicsComposition* composition=this->GetBoundsComposition()->FindComposition(location, false);
				if(composition)
				{
					GuiControl* control=composition->GetRelatedControl();
					while(control)
					{
						if(control->GetTooltipControl())
						{
							return control;
						}
						control=control->GetParent();
					}
				}
				return 0;
			}

			void GuiControlHost::MoveIntoTooltipControl(GuiControl* tooltipControl, Point location)
			{
				if(tooltipLocation!=location)
				{
					tooltipLocation=location;
					{
						GuiControl* currentOwner=GetApplication()->GetTooltipOwner();
						if(currentOwner && currentOwner!=tooltipControl)
						{
							if(tooltipCloseDelay)
							{
								tooltipCloseDelay->Cancel();
								tooltipCloseDelay=0;
							}
							GetApplication()->DelayExecuteInMainThread([=]()
							{
								currentOwner->CloseTooltip();
							}, TooltipDelayCloseTime);
						}
					}
					if(!tooltipControl)
					{
						if(tooltipOpenDelay)
						{
							tooltipOpenDelay->Cancel();
							tooltipOpenDelay=0;
						}
					}
					else if(tooltipOpenDelay)
					{
						tooltipOpenDelay->Delay(TooltipDelayOpenTime);
					}
					else if(GetApplication()->GetTooltipOwner()!=tooltipControl)
					{
						tooltipOpenDelay=GetApplication()->DelayExecuteInMainThread([this]()
						{
							GuiControl* owner=GetTooltipOwner(tooltipLocation);
							if(owner)
							{
								Point offset=owner->GetBoundsComposition()->GetGlobalBounds().LeftTop();
								Point p(tooltipLocation.x-offset.x, tooltipLocation.y-offset.y+24);
								owner->DisplayTooltip(p);
								tooltipOpenDelay=0;

								tooltipCloseDelay=GetApplication()->DelayExecuteInMainThread([this, owner]()
								{
									owner->CloseTooltip();
								}, TooltipDelayLifeTime);
							}
						}, TooltipDelayOpenTime);
					}
				}
			}

			void GuiControlHost::MouseMoving(const NativeWindowMouseInfo& info)
			{
				if(!info.left && !info.middle && !info.right)
				{
					GuiControl* tooltipControl=GetTooltipOwner(tooltipLocation);
					MoveIntoTooltipControl(tooltipControl, Point(info.x, info.y));
				}
			}

			void GuiControlHost::MouseLeaved()
			{
				MoveIntoTooltipControl(0, Point(-1, -1));
			}

			void GuiControlHost::Moved()
			{
				OnVisualStatusChanged();
			}

			void GuiControlHost::Enabled()
			{
				GuiControl::SetEnabled(true);
				OnVisualStatusChanged();
			}

			void GuiControlHost::Disabled()
			{
				GuiControl::SetEnabled(false);
				OnVisualStatusChanged();
			}

			void GuiControlHost::GotFocus()
			{
				WindowGotFocus.Execute(GetNotifyEventArguments());
				OnVisualStatusChanged();
			}

			void GuiControlHost::LostFocus()
			{
				WindowLostFocus.Execute(GetNotifyEventArguments());
				OnVisualStatusChanged();
			}

			void GuiControlHost::Activated()
			{
				WindowActivated.Execute(GetNotifyEventArguments());
				OnVisualStatusChanged();
			}

			void GuiControlHost::Deactivated()
			{
				WindowDeactivated.Execute(GetNotifyEventArguments());
				OnVisualStatusChanged();
			}

			void GuiControlHost::Opened()
			{
				WindowOpened.Execute(GetNotifyEventArguments());
			}

			void GuiControlHost::Closing(bool& cancel)
			{
				GuiRequestEventArgs arguments(boundsComposition);
				arguments.cancel=cancel;
				WindowClosing.Execute(arguments);
				if(!arguments.handled)
				{
					cancel=arguments.cancel;
				}
			}

			void GuiControlHost::Closed()
			{
				WindowClosed.Execute(GetNotifyEventArguments());
			}

			void GuiControlHost::Destroying()
			{
				WindowDestroying.Execute(GetNotifyEventArguments());
				SetNativeWindow(0);
			}

			void GuiControlHost::UpdateClientSizeAfterRendering(Size clientSize)
			{
				SetClientSize(clientSize);
			}

			GuiControlHost::GuiControlHost(theme::ThemeName themeName)
				:GuiControl(themeName)
			{
				boundsComposition->SetAlignmentToParent(Margin(0, 0, 0, 0));
				
				WindowGotFocus.SetAssociatedComposition(boundsComposition);
				WindowLostFocus.SetAssociatedComposition(boundsComposition);
				WindowActivated.SetAssociatedComposition(boundsComposition);
				WindowDeactivated.SetAssociatedComposition(boundsComposition);
				WindowOpened.SetAssociatedComposition(boundsComposition);
				WindowClosing.SetAssociatedComposition(boundsComposition);
				WindowClosed.SetAssociatedComposition(boundsComposition);
				WindowDestroying.SetAssociatedComposition(boundsComposition);

				host=new GuiGraphicsHost(this, boundsComposition);
				sharedPtrDestructorProc = 0;
			}

			GuiControlHost::~GuiControlHost()
			{
				FinalizeInstanceRecursively(this);
				OnBeforeReleaseGraphicsHost();
				delete host;
			}

			compositions::GuiGraphicsHost* GuiControlHost::GetGraphicsHost()
			{
				return host;
			}

			compositions::GuiGraphicsComposition* GuiControlHost::GetMainComposition()
			{
				return host->GetMainComposition();
			}

			INativeWindow* GuiControlHost::GetNativeWindow()
			{
				return host->GetNativeWindow();
			}

			void GuiControlHost::SetNativeWindow(INativeWindow* window)
			{
				if(host->GetNativeWindow())
				{
					host->GetNativeWindow()->UninstallListener(this);
				}
				host->SetNativeWindow(window);
				if(host->GetNativeWindow())
				{
					host->GetNativeWindow()->InstallListener(this);
				}
				OnNativeWindowChanged();
			}

			void GuiControlHost::ForceCalculateSizeImmediately()
			{
				boundsComposition->ForceCalculateSizeImmediately();
				SetBounds(GetBounds());
			}

			bool GuiControlHost::GetEnabled()
			{
				if(host->GetNativeWindow())
				{
					return host->GetNativeWindow()->IsEnabled();
				}
				else
				{
					return false;
				}
			}

			void GuiControlHost::SetEnabled(bool value)
			{
				if(host->GetNativeWindow())
				{
					if(value)
					{
						host->GetNativeWindow()->Enable();
					}
					else
					{
						host->GetNativeWindow()->Disable();
					}
				}
			}

			bool GuiControlHost::GetFocused()
			{
				if(host->GetNativeWindow())
				{
					return host->GetNativeWindow()->IsFocused();
				}
				else
				{
					return false;
				}
			}

			void GuiControlHost::SetFocused()
			{
				if(host->GetNativeWindow())
				{
					host->GetNativeWindow()->SetFocus();
				}
			}

			bool GuiControlHost::GetActivated()
			{
				if(host->GetNativeWindow())
				{
					return host->GetNativeWindow()->IsActivated();
				}
				else
				{
					return false;
				}
			}

			void GuiControlHost::SetActivated()
			{
				if(host->GetNativeWindow())
				{
					host->GetNativeWindow()->SetActivate();
				}
			}

			bool GuiControlHost::GetShowInTaskBar()
			{
				if(host->GetNativeWindow())
				{
					return host->GetNativeWindow()->IsAppearedInTaskBar();
				}
				else
				{
					return false;
				}
			}

			void GuiControlHost::SetShowInTaskBar(bool value)
			{
				if(host->GetNativeWindow())
				{
					if(value)
					{
						host->GetNativeWindow()->ShowInTaskBar();
					}
					else
					{
						host->GetNativeWindow()->HideInTaskBar();
					}
				}
			}

			bool GuiControlHost::GetEnabledActivate()
			{
				if(host->GetNativeWindow())
				{
					return host->GetNativeWindow()->IsEnabledActivate();
				}
				else
				{
					return false;
				}
			}

			void GuiControlHost::SetEnabledActivate(bool value)
			{
				if(host->GetNativeWindow())
				{
					if(value)
					{
						host->GetNativeWindow()->EnableActivate();
					}
					else
					{
						host->GetNativeWindow()->DisableActivate();
					}
				}
			}

			bool GuiControlHost::GetTopMost()
			{
				if(host->GetNativeWindow())
				{
					return host->GetNativeWindow()->GetTopMost();
				}
				else
				{
					return false;
				}
			}

			void GuiControlHost::SetTopMost(bool topmost)
			{
				if(host->GetNativeWindow())
				{
					host->GetNativeWindow()->SetTopMost(topmost);
				}
			}

			compositions::IGuiShortcutKeyManager* GuiControlHost::GetShortcutKeyManager()
			{
				return host->GetShortcutKeyManager();
			}

			void GuiControlHost::SetShortcutKeyManager(compositions::IGuiShortcutKeyManager* value)
			{
				host->SetShortcutKeyManager(value);
			}

			compositions::GuiGraphicsTimerManager* GuiControlHost::GetTimerManager()
			{
				return host->GetTimerManager();
			}

			Size GuiControlHost::GetClientSize()
			{
				if(host->GetNativeWindow())
				{
					return host->GetNativeWindow()->GetClientSize();
				}
				else
				{
					return Size(0, 0);
				}
			}

			void GuiControlHost::SetClientSize(Size value)
			{
				if(host->GetNativeWindow())
				{
					host->GetNativeWindow()->SetClientSize(value);
				}
			}

			Rect GuiControlHost::GetBounds()
			{
				if(host->GetNativeWindow())
				{
					return host->GetNativeWindow()->GetBounds();
				}
				else
				{
					return Rect();
				}
			}

			void GuiControlHost::SetBounds(Rect value)
			{
				if(host->GetNativeWindow())
				{
					host->GetNativeWindow()->SetBounds(value);
				}
			}

			GuiControlHost* GuiControlHost::GetRelatedControlHost()
			{
				return this;
			}

			const WString& GuiControlHost::GetText()
			{
				WString result;
				if(host->GetNativeWindow())
				{
					result=host->GetNativeWindow()->GetTitle();
				}
				if(result!=GuiControl::GetText())
				{
					GuiControl::SetText(result);
				}
				return GuiControl::GetText();
			}

			void GuiControlHost::SetText(const WString& value)
			{
				if(host->GetNativeWindow())
				{
					host->GetNativeWindow()->SetTitle(value);
					GuiControl::SetText(value);
				}
			}

			INativeScreen* GuiControlHost::GetRelatedScreen()
			{
				if(host->GetNativeWindow())
				{
					return GetCurrentController()->ScreenService()->GetScreen(host->GetNativeWindow());
				}
				else
				{
					return 0;
				}
			}

			void GuiControlHost::Show()
			{
				if(host->GetNativeWindow())
				{
					host->GetNativeWindow()->Show();
				}
			}

			void GuiControlHost::ShowDeactivated()
			{
				if(host->GetNativeWindow())
				{
					host->GetNativeWindow()->ShowDeactivated();
				}
			}

			void GuiControlHost::ShowRestored()
			{
				if(host->GetNativeWindow())
				{
					host->GetNativeWindow()->ShowRestored();
				}
			}

			void GuiControlHost::ShowMaximized()
			{
				if(host->GetNativeWindow())
				{
					host->GetNativeWindow()->ShowMaximized();
				}
			}

			void GuiControlHost::ShowMinimized()
			{
				if(host->GetNativeWindow())
				{
					host->GetNativeWindow()->ShowMinimized();
				}
			}

			void GuiControlHost::Hide()
			{
				if(host->GetNativeWindow())
				{
					host->GetNativeWindow()->Hide();
				}
			}

			void GuiControlHost::Close()
			{
				INativeWindow* window=host->GetNativeWindow();
				if(window)
				{
					if(GetCurrentController()->WindowService()->GetMainWindow()!=window)
					{
						window->Hide();
					}
					else
					{
						SetNativeWindow(0);
						GetCurrentController()->WindowService()->DestroyNativeWindow(window);
					}
				}
			}

			bool GuiControlHost::GetOpening()
			{
				INativeWindow* window=host->GetNativeWindow();
				if(window)
				{
					return window->IsVisible();
				}
				return false;
			}

/***********************************************************************
GuiWindow
***********************************************************************/

			void GuiWindow::BeforeControlTemplateUninstalled_()
			{
			}

			void GuiWindow::AfterControlTemplateInstalled_(bool initialize)
			{
				auto ct = GetControlTemplateObject(true);
#define FIX_WINDOW_PROPERTY(VARIABLE, NAME) \
				switch (ct->Get ## NAME ## Option()) \
				{ \
				case templates::BoolOption::AlwaysTrue: \
					VARIABLE = true; \
					break; \
				case templates::BoolOption::AlwaysFalse: \
					VARIABLE = false; \
					break; \
				default:; \
				} \

				FIX_WINDOW_PROPERTY(hasMaximizedBox, MaximizedBox)
				FIX_WINDOW_PROPERTY(hasMinimizedBox, MinimizedBox)
				FIX_WINDOW_PROPERTY(hasBorder, Border)
				FIX_WINDOW_PROPERTY(hasSizeBox, SizeBox)
				FIX_WINDOW_PROPERTY(isIconVisible, IconVisible)
				FIX_WINDOW_PROPERTY(hasTitleBar, TitleBar)

#undef FIX_WINDOW_PROPERTY
				ct->SetMaximizedBox(hasMaximizedBox);
				ct->SetMinimizedBox(hasMinimizedBox);
				ct->SetBorder(hasBorder);
				ct->SetSizeBox(hasSizeBox);
				ct->SetIconVisible(isIconVisible);
				ct->SetTitleBar(hasTitleBar);
				ct->SetMaximized(GetNativeWindow()->GetSizeState() != INativeWindow::Maximized);
				SyncNativeWindowProperties();
			}

			void GuiWindow::SyncNativeWindowProperties()
			{
				if (auto window = GetNativeWindow())
				{
					if (GetControlTemplateObject(true)->GetCustomFrameEnabled())
					{
						window->EnableCustomFrameMode();
						window->SetBorder(false);
					}
					else
					{
						window->DisableCustomFrameMode();
						window->SetBorder(hasBorder);
					}

					window->SetMaximizedBox(hasMaximizedBox);
					window->SetMinimizedBox(hasMinimizedBox);
					window->SetSizeBox(hasSizeBox);
					window->SetIconVisible(isIconVisible);
					window->SetTitleBar(hasTitleBar);
				}
			}

			void GuiWindow::Moved()
			{
				GuiControlHost::Moved();
				GetControlTemplateObject(true)->SetMaximized(GetNativeWindow()->GetSizeState() != INativeWindow::Maximized);
			}

			void GuiWindow::OnNativeWindowChanged()
			{
				SyncNativeWindowProperties();
				GuiControlHost::OnNativeWindowChanged();
			}

			void GuiWindow::OnVisualStatusChanged()
			{
				GuiControlHost::OnVisualStatusChanged();
			}

			void GuiWindow::MouseClickedOnOtherWindow(GuiWindow* window)
			{
			}

			compositions::GuiGraphicsComposition* GuiWindow::GetAltComposition()
			{
				return boundsComposition;
			}

			compositions::IGuiAltActionHost* GuiWindow::GetPreviousAltHost()
			{
				return previousAltHost;
			}

			void GuiWindow::OnActivatedAltHost(IGuiAltActionHost* previousHost)
			{
				previousAltHost = previousHost;
			}

			void GuiWindow::OnDeactivatedAltHost()
			{
				previousAltHost = 0;
			}

			void GuiWindow::CollectAltActions(collections::Group<WString, IGuiAltAction*>& actions)
			{
				IGuiAltActionHost::CollectAltActionsFromControl(this, actions);
			}

			GuiWindow::GuiWindow(theme::ThemeName themeName)
				:GuiControlHost(themeName)
				,previousAltHost(0)
			{
				INativeWindow* window=GetCurrentController()->WindowService()->CreateNativeWindow();
				SetNativeWindow(window);
				GetApplication()->RegisterWindow(this);
				ClipboardUpdated.SetAssociatedComposition(boundsComposition);
			}

			GuiWindow::~GuiWindow()
			{
				FinalizeAggregation();
				GetApplication()->UnregisterWindow(this);
				INativeWindow* window=host->GetNativeWindow();
				if(window)
				{
					SetNativeWindow(0);
					GetCurrentController()->WindowService()->DestroyNativeWindow(window);
				}
			}

			IDescriptable* GuiWindow::QueryService(const WString& identifier)
			{
				if (identifier == IGuiAltActionHost::Identifier)
				{
					return (IGuiAltActionHost*)this;
				}
				else
				{
					return GuiControlHost::QueryService(identifier);
				}
			}

			void GuiWindow::MoveToScreenCenter()
			{
				MoveToScreenCenter(GetRelatedScreen());
			}

			void GuiWindow::MoveToScreenCenter(INativeScreen* screen)
			{
				if (screen)
				{
					Rect screenBounds = screen->GetClientBounds();
					Rect windowBounds = GetBounds();
					SetBounds(
						Rect(
							Point(
								screenBounds.Left() + (screenBounds.Width() - windowBounds.Width()) / 2,
								screenBounds.Top() + (screenBounds.Height() - windowBounds.Height()) / 2
							),
							windowBounds.GetSize()
						)
					);
				}
			}

#define IMPL_WINDOW_PROPERTY(VARIABLE, NAME, CONDITION_BREAK) \
			bool GuiWindow::Get ## NAME() \
			{ \
				return VARIABLE; \
			} \
			void GuiWindow::Set ## NAME(bool visible) \
			{ \
				auto ct = GetControlTemplateObject(true); \
				if (ct->Get ## NAME ## Option() == templates::BoolOption::Customizable) \
				{ \
					VARIABLE = visible; \
					ct->Set ## NAME(visible); \
					if (auto window = GetNativeWindow()) \
					{ \
						CONDITION_BREAK \
						window->Set ## NAME(visible); \
					} \
				} \
			} \

#define IMPL_WINDOW_PROPERTY_EMPTY_CONDITION
#define IMPL_WINDOW_PROPERTY_BORDER_CONDITION if (ct->GetCustomFrameEnabled()) return;

			IMPL_WINDOW_PROPERTY(hasMaximizedBox, MaximizedBox, IMPL_WINDOW_PROPERTY_EMPTY_CONDITION)
			IMPL_WINDOW_PROPERTY(hasMinimizedBox, MinimizedBox, IMPL_WINDOW_PROPERTY_EMPTY_CONDITION)
			IMPL_WINDOW_PROPERTY(hasBorder, Border, IMPL_WINDOW_PROPERTY_BORDER_CONDITION)
			IMPL_WINDOW_PROPERTY(hasSizeBox, SizeBox, IMPL_WINDOW_PROPERTY_EMPTY_CONDITION)
			IMPL_WINDOW_PROPERTY(isIconVisible, IconVisible, IMPL_WINDOW_PROPERTY_EMPTY_CONDITION)
			IMPL_WINDOW_PROPERTY(hasTitleBar, TitleBar, IMPL_WINDOW_PROPERTY_EMPTY_CONDITION)

#undef IMPL_WINDOW_PROPERTY_BORDER_CONDITION
#undef IMPL_WINDOW_PROPERTY_EMPTY_CONDITION
#undef IMPL_WINDOW_PROPERTY

			void GuiWindow::ShowModal(GuiWindow* owner, const Func<void()>& callback)
			{
				owner->SetEnabled(false);
				GetNativeWindow()->SetParent(owner->GetNativeWindow());
				auto container = MakePtr<IGuiGraphicsEventHandler::Container>();
				container->handler = WindowClosed.AttachLambda([=](GuiGraphicsComposition* sender, GuiEventArgs& arguments)
				{
					GetApplication()->InvokeInMainThread(this, [=]()
					{
						WindowClosed.Detach(container->handler);
						container->handler = nullptr;
						GetNativeWindow()->SetParent(0);
						callback();
						owner->SetEnabled(true);
						owner->SetActivated();
					});
				});
				Show();
			}

			void GuiWindow::ShowModalAndDelete(GuiWindow* owner, const Func<void()>& callback)
			{
				ShowModal(owner, [=]()
				{
					callback();
					delete this;
				});
			}

			Ptr<reflection::description::IAsync> GuiWindow::ShowModalAsync(GuiWindow* owner)
			{
				auto future = IFuture::Create();
				ShowModal(owner, [promise = future->GetPromise()]()
				{
					promise->SendResult({});
				});
				return future;
			}

/***********************************************************************
GuiPopup
***********************************************************************/

			void GuiPopup::UpdateClientSizeAfterRendering(Size clientSize)
			{
				if (popupType == -1)
				{
					GuiWindow::UpdateClientSizeAfterRendering(clientSize);
				}
				else
				{
					auto window = GetNativeWindow();
					auto position = CalculatePopupPosition(clientSize, popupType, popupInfo);
					window->SetBounds(Rect(position, clientSize));
				}
			}

			void GuiPopup::MouseClickedOnOtherWindow(GuiWindow* window)
			{
				Hide();
			}

			void GuiPopup::PopupOpened(compositions::GuiGraphicsComposition* sender, compositions::GuiEventArgs& arguments)
			{
				GetApplication()->RegisterPopupOpened(this);
			}

			void GuiPopup::PopupClosed(compositions::GuiGraphicsComposition* sender, compositions::GuiEventArgs& arguments)
			{
				popupType = -1;
				GetApplication()->RegisterPopupClosed(this);
				if(auto window = GetNativeWindow())
				{
					window->SetParent(nullptr);
				}
			}

			Point GuiPopup::CalculatePopupPosition(Size size, Point location, INativeScreen* screen)
			{
				Rect screenBounds = screen->GetClientBounds();

				if (location.x < screenBounds.x1)
				{
					location.x = screenBounds.x1;
				}
				else if (location.x + size.x > screenBounds.x2)
				{
					location.x = screenBounds.x2 - size.x;
				}

				if (location.y < screenBounds.y1)
				{
					location.y = screenBounds.y1;
				}
				else if (location.y + size.y > screenBounds.y2)
				{
					location.y = screenBounds.y2 - size.y;
				}

				return location;
			}

			bool GuiPopup::IsClippedByScreen(Size size, Point location, INativeScreen* screen)
			{
				Rect screenBounds = screen->GetClientBounds();
				Rect windowBounds(location, size);
				return !screenBounds.Contains(windowBounds.LeftTop()) || !screenBounds.Contains(windowBounds.RightBottom());
			}

			Point GuiPopup::CalculatePopupPosition(Size size, GuiControl* control, INativeWindow* controlWindow, Rect bounds, bool preferredTopBottomSide)
			{
				Point controlClientOffset = control->GetBoundsComposition()->GetGlobalBounds().LeftTop();
				Point controlWindowOffset = controlWindow->GetClientBoundsInScreen().LeftTop();
				bounds.x1 += controlClientOffset.x + controlWindowOffset.x;
				bounds.x2 += controlClientOffset.x + controlWindowOffset.x;
				bounds.y1 += controlClientOffset.y + controlWindowOffset.y;
				bounds.y2 += controlClientOffset.y + controlWindowOffset.y;

				Point locations[4];
				if (preferredTopBottomSide)
				{
					locations[0] = Point(bounds.x1, bounds.y2);
					locations[1] = Point(bounds.x2 - size.x, bounds.y2);
					locations[2] = Point(bounds.x1, bounds.y1 - size.y);
					locations[3] = Point(bounds.x2 - size.x, bounds.y1 - size.y);
				}
				else
				{
					locations[0] = Point(bounds.x2, bounds.y1);
					locations[1] = Point(bounds.x2, bounds.y2 - size.y);
					locations[2] = Point(bounds.x1 - size.x, bounds.y1);
					locations[3] = Point(bounds.x1 - size.x, bounds.y2 - size.y);
				}

				auto screen = GetCurrentController()->ScreenService()->GetScreen(controlWindow);
				for (vint i = 0; i < 4; i++)
				{
					if (!IsClippedByScreen(size, locations[i], screen))
					{
						return CalculatePopupPosition(size, locations[i], screen);
					}
				}
				return CalculatePopupPosition(size, locations[0], screen);
			}

			Point GuiPopup::CalculatePopupPosition(Size size, GuiControl* control, INativeWindow* controlWindow, Point location)
			{
				Point locations[4];
				Rect controlBounds = control->GetBoundsComposition()->GetGlobalBounds();

				Point controlClientOffset = controlWindow->GetClientBoundsInScreen().LeftTop();
				vint x = controlBounds.x1 + controlClientOffset.x + location.x;
				vint y = controlBounds.y1 + controlClientOffset.y + location.y;
				return CalculatePopupPosition(size, Point(x, y), GetCurrentController()->ScreenService()->GetScreen(controlWindow));
			}

			Point GuiPopup::CalculatePopupPosition(Size size, GuiControl* control, INativeWindow* controlWindow, bool preferredTopBottomSide)
			{
				Rect bounds(Point(0, 0), control->GetBoundsComposition()->GetBounds().GetSize());
				return CalculatePopupPosition(size, control, controlWindow, bounds, preferredTopBottomSide);
			}

			Point GuiPopup::CalculatePopupPosition(Size size, vint popupType, const PopupInfo& popupInfo)
			{
				switch (popupType)
				{
				case 1:
					return CalculatePopupPosition(size, popupInfo._1.location, popupInfo._1.screen);
				case 2:
					return CalculatePopupPosition(size, popupInfo._2.control, popupInfo._2.controlWindow, popupInfo._2.bounds, popupInfo._2.preferredTopBottomSide);
				case 3:
					return CalculatePopupPosition(size, popupInfo._3.control, popupInfo._3.controlWindow, popupInfo._3.location);
				case 4:
					return CalculatePopupPosition(size, popupInfo._4.control, popupInfo._4.controlWindow, popupInfo._4.preferredTopBottomSide);
				default:
					CHECK_FAIL(L"vl::presentation::controls::GuiPopup::CalculatePopupPosition(Size, const PopupInfo&)#Internal error.");
				}
			}

			void GuiPopup::ShowPopupInternal()
			{
				auto window = GetNativeWindow();
				UpdateClientSizeAfterRendering(window->GetBounds().GetSize());
				switch (popupType)
				{
				case 2: window->SetParent(popupInfo._2.controlWindow); break;
				case 3: window->SetParent(popupInfo._3.controlWindow); break;
				case 4: window->SetParent(popupInfo._4.controlWindow); break;
				}
				ShowDeactivated();
			}

			GuiPopup::GuiPopup(theme::ThemeName themeName)
				:GuiWindow(themeName)
			{
				SetMinimizedBox(false);
				SetMaximizedBox(false);
				SetSizeBox(false);
				SetTitleBar(false);
				SetShowInTaskBar(false);

				WindowOpened.AttachMethod(this, &GuiPopup::PopupOpened);
				WindowClosed.AttachMethod(this, &GuiPopup::PopupClosed);
			}

			GuiPopup::~GuiPopup()
			{
				GetApplication()->RegisterPopupClosed(this);
			}

			void GuiPopup::ShowPopup(Point location, INativeScreen* screen)
			{
				if (auto window = GetNativeWindow())
				{
					if (!screen)
					{
						SetBounds(Rect(location, GetBounds().GetSize()));
						screen = GetCurrentController()->ScreenService()->GetScreen(window);
					}

					popupType = 1;
					popupInfo._1.location = location;
					popupInfo._1.screen = screen;
					ShowPopupInternal();
				}
			}
			
			void GuiPopup::ShowPopup(GuiControl* control, Rect bounds, bool preferredTopBottomSide)
			{
				if (auto window = GetNativeWindow())
				{
					if (auto controlHost = control->GetBoundsComposition()->GetRelatedControlHost())
					{
						if (auto controlWindow = controlHost->GetNativeWindow())
						{
							popupType = 2;
							popupInfo._2.control = control;
							popupInfo._2.controlWindow = controlWindow;
							popupInfo._2.bounds = bounds;
							popupInfo._2.preferredTopBottomSide = preferredTopBottomSide;
							ShowPopupInternal();
						}
					}
				}
			}

			void GuiPopup::ShowPopup(GuiControl* control, Point location)
			{
				if (auto window = GetNativeWindow())
				{
					if (auto controlHost = control->GetBoundsComposition()->GetRelatedControlHost())
					{
						if (auto controlWindow = controlHost->GetNativeWindow())
						{
							popupType = 3;
							popupInfo._3.control = control;
							popupInfo._3.controlWindow = controlWindow;
							popupInfo._3.location = location;
							ShowPopupInternal();
						}
					}
				}
			}

			void GuiPopup::ShowPopup(GuiControl* control, bool preferredTopBottomSide)
			{
				if (auto window = GetNativeWindow())
				{
					if (auto controlHost = control->GetBoundsComposition()->GetRelatedControlHost())
					{
						if (auto controlWindow = controlHost->GetNativeWindow())
						{
							popupType = 4;
							popupInfo._4.control = control;
							popupInfo._4.controlWindow = controlWindow;
							popupInfo._4.preferredTopBottomSide = preferredTopBottomSide;
							ShowPopupInternal();
						}
					}
				}
			}

/***********************************************************************
GuiPopup
***********************************************************************/

			void GuiTooltip::GlobalTimer()
			{
				SetClientSize(GetClientSize());
			}

			void GuiTooltip::TooltipOpened(compositions::GuiGraphicsComposition* sender, compositions::GuiEventArgs& arguments)
			{
			}

			void GuiTooltip::TooltipClosed(compositions::GuiGraphicsComposition* sender, compositions::GuiEventArgs& arguments)
			{
				SetTemporaryContentControl(0);
			}

			GuiTooltip::GuiTooltip(theme::ThemeName themeName)
				:GuiPopup(themeName)
				,temporaryContentControl(0)
			{
				containerComposition->SetMinSizeLimitation(GuiGraphicsComposition::LimitToElementAndChildren);
				containerComposition->SetPreferredMinSize(Size(20, 10));
				GetCurrentController()->CallbackService()->InstallListener(this);

				WindowOpened.AttachMethod(this, &GuiTooltip::TooltipOpened);
				WindowClosed.AttachMethod(this, &GuiTooltip::TooltipClosed);
			}

			GuiTooltip::~GuiTooltip()
			{
				GetCurrentController()->CallbackService()->UninstallListener(this);
			}

			vint GuiTooltip::GetPreferredContentWidth()
			{
				return containerComposition->GetPreferredMinSize().x;
			}

			void GuiTooltip::SetPreferredContentWidth(vint value)
			{
				containerComposition->SetPreferredMinSize(Size(value, 10));
			}

			GuiControl* GuiTooltip::GetTemporaryContentControl()
			{
				return temporaryContentControl;
			}

			void GuiTooltip::SetTemporaryContentControl(GuiControl* control)
			{
				if(temporaryContentControl && HasChild(temporaryContentControl))
				{
					containerComposition->RemoveChild(temporaryContentControl->GetBoundsComposition());
					temporaryContentControl=0;
				}
				temporaryContentControl=control;
				if(control)
				{
					control->GetBoundsComposition()->SetAlignmentToParent(Margin(0, 0, 0, 0));
					AddChild(control);
				}
			}
		}
	}
}


/***********************************************************************
.\GRAPHICSCOMPOSITION\GUIGRAPHICSCOMPOSITION.CPP
***********************************************************************/

namespace vl
{
	namespace presentation
	{
		namespace compositions
		{
			using namespace reflection::description;
			using namespace collections;
			using namespace controls;
			using namespace elements;

/***********************************************************************
GuiSharedSizeItemComposition
***********************************************************************/

			void GuiSharedSizeItemComposition::Update()
			{
				if (parentRoot)
				{
					parentRoot->ForceCalculateSizeImmediately();
				}
				InvokeOnCompositionStateChanged();
			}

			void GuiSharedSizeItemComposition::OnParentLineChanged()
			{
				GuiBoundsComposition::OnParentLineChanged();
				if (parentRoot)
				{
					parentRoot->childItems.Remove(this);
					parentRoot = 0;
				}

				auto current = GetParent();
				while (current)
				{
					if (auto item = dynamic_cast<GuiSharedSizeItemComposition*>(current))
					{
						break;
					}
					else if (auto root = dynamic_cast<GuiSharedSizeRootComposition*>(current))
					{
						parentRoot = root;
						break;
					}
					current = current->GetParent();
				}

				if (parentRoot)
				{
					parentRoot->childItems.Add(this);
				}
			}

			GuiSharedSizeItemComposition::GuiSharedSizeItemComposition()
				:parentRoot(0)
				, sharedWidth(false)
				, sharedHeight(false)
			{
				SetMinSizeLimitation(GuiGraphicsComposition::LimitToElementAndChildren);
			}

			GuiSharedSizeItemComposition::~GuiSharedSizeItemComposition()
			{
			}

			const WString& GuiSharedSizeItemComposition::GetGroup()
			{
				return group;
			}

			void GuiSharedSizeItemComposition::SetGroup(const WString& value)
			{
				if (group != value)
				{
					group = value;
					Update();
				}
			}

			bool GuiSharedSizeItemComposition::GetSharedWidth()
			{
				return sharedWidth;
			}

			void GuiSharedSizeItemComposition::SetSharedWidth(bool value)
			{
				if (sharedWidth != value)
				{
					sharedWidth = value;
					Update();
				}
			}

			bool GuiSharedSizeItemComposition::GetSharedHeight()
			{
				return sharedHeight;
			}

			void GuiSharedSizeItemComposition::SetSharedHeight(bool value)
			{
				if (sharedHeight != value)
				{
					sharedHeight = value;
					Update();
				}
			}

/***********************************************************************
GuiSharedSizeRootComposition
***********************************************************************/

			void GuiSharedSizeRootComposition::AddSizeComponent(collections::Dictionary<WString, vint>& sizes, const WString& group, vint sizeComponent)
			{
				vint index = sizes.Keys().IndexOf(group);
				if (index == -1)
				{
					sizes.Add(group, sizeComponent);
				}
				else if (sizes.Values().Get(index) < sizeComponent)
				{
					sizes.Set(group, sizeComponent);
				}
			}

			void GuiSharedSizeRootComposition::CollectSizes(collections::Dictionary<WString, vint>& widths, collections::Dictionary<WString, vint>& heights)
			{
				FOREACH(GuiSharedSizeItemComposition*, item, childItems)
				{
					auto group = item->GetGroup();
					auto minSize = item->GetPreferredMinSize();
					item->SetPreferredMinSize(Size(0, 0));
					auto size = item->GetPreferredBounds().GetSize();

					if (item->GetSharedWidth())
					{
						AddSizeComponent(widths, group, size.x);
					}
					if (item->GetSharedHeight())
					{
						AddSizeComponent(heights, group, size.y);
					}

					item->SetPreferredMinSize(minSize);
				}
			}

			void GuiSharedSizeRootComposition::AlignSizes(collections::Dictionary<WString, vint>& widths, collections::Dictionary<WString, vint>& heights)
			{
				FOREACH(GuiSharedSizeItemComposition*, item, childItems)
				{
					auto group = item->GetGroup();
					auto size = item->GetPreferredMinSize();

					if (item->GetSharedWidth())
					{
						size.x = widths[group];
					}
					if (item->GetSharedHeight())
					{
						size.y = heights[group];
					}

					item->SetPreferredMinSize(size);
				}
			}

			void GuiSharedSizeRootComposition::UpdateBounds()
			{

			}

			GuiSharedSizeRootComposition::GuiSharedSizeRootComposition()
			{
			}

			GuiSharedSizeRootComposition::~GuiSharedSizeRootComposition()
			{
			}

			void GuiSharedSizeRootComposition::ForceCalculateSizeImmediately()
			{
				itemWidths.Clear();
				itemHeights.Clear();

				CollectSizes(itemWidths, itemHeights);
				AlignSizes(itemWidths, itemHeights);
				GuiBoundsComposition::ForceCalculateSizeImmediately();
			}

			Rect GuiSharedSizeRootComposition::GetBounds()
			{
				Dictionary<WString, vint> widths, heights;
				CollectSizes(widths, heights);
				bool minSizeModified = CompareEnumerable(itemWidths, widths) != 0 || CompareEnumerable(itemHeights, heights) != 0;

				if (minSizeModified)
				{
					CopyFrom(itemWidths, widths);
					CopyFrom(itemHeights, heights);
					AlignSizes(itemWidths, itemHeights);
					GuiBoundsComposition::ForceCalculateSizeImmediately();
				}
				return GuiBoundsComposition::GetBounds();
			}

/***********************************************************************
GuiRepeatCompositionBase
***********************************************************************/

			void GuiRepeatCompositionBase::OnItemChanged(vint index, vint oldCount, vint newCount)
			{
				if (itemTemplate && itemSource)
				{
					for (vint i = oldCount - 1; i >= 0; i--)
					{
						RemoveItem(index + i);
					}

					for (vint i = 0; i < newCount; i++)
					{
						InstallItem(index + i);
					}
				}
			}

			void GuiRepeatCompositionBase::RemoveItem(vint index)
			{
				GuiItemEventArgs arguments(dynamic_cast<GuiGraphicsComposition*>(this));
				arguments.itemIndex = index;
				ItemRemoved.Execute(arguments);

				auto item = RemoveRepeatComposition(index);
				SafeDeleteComposition(item);
			}

			void GuiRepeatCompositionBase::InstallItem(vint index)
			{
				auto source = itemSource->Get(index);
				auto templateItem = itemTemplate(source);
				auto item = InsertRepeatComposition(index);

				templateItem->SetAlignmentToParent(Margin(0, 0, 0, 0));
				item->SetMinSizeLimitation(GuiGraphicsComposition::LimitToElementAndChildren);
				item->AddChild(templateItem);

				GuiItemEventArgs arguments(dynamic_cast<GuiGraphicsComposition*>(this));
				arguments.itemIndex = index;
				ItemInserted.Execute(arguments);
			}

			void GuiRepeatCompositionBase::ClearItems()
			{
				for (vint i = GetRepeatCompositionCount() - 1; i >= 0; i--)
				{
					RemoveItem(i);
				}
			}

			void GuiRepeatCompositionBase::InstallItems()
			{
				if (itemTemplate && itemSource)
				{
					vint count = itemSource->GetCount();
					for (vint i = 0; i < count; i++)
					{
						InstallItem(i);
					}
				}
			}

			GuiRepeatCompositionBase::GuiRepeatCompositionBase()
			{
			}

			GuiRepeatCompositionBase::~GuiRepeatCompositionBase()
			{
			}

			GuiRepeatCompositionBase::ItemStyleProperty GuiRepeatCompositionBase::GetItemTemplate()
			{
				return itemTemplate;
			}

			void GuiRepeatCompositionBase::SetItemTemplate(ItemStyleProperty value)
			{
				ClearItems();
				itemTemplate = value;
				if (itemTemplate && itemSource)
				{
					InstallItems();
				}
			}

			Ptr<IValueEnumerable> GuiRepeatCompositionBase::GetItemSource()
			{
				return itemSource;
			}

			void GuiRepeatCompositionBase::SetItemSource(Ptr<IValueEnumerable> value)
			{
				if (value != itemSource)
				{
					if (itemChangedHandler)
					{
						itemSource.Cast<IValueObservableList>()->ItemChanged.Remove(itemChangedHandler);
					}

					ClearItems();
					itemSource = value.Cast<IValueList>();
					if (!itemSource && value)
					{
						itemSource = IValueList::Create(GetLazyList<Value>(value));
					}

					if (itemTemplate && itemSource)
					{
						InstallItems();
					}
					if (auto observable = itemSource.Cast<IValueObservableList>())
					{
						itemChangedHandler = observable->ItemChanged.Add(this, &GuiRepeatCompositionBase::OnItemChanged);
					}
				}
			}

/***********************************************************************
GuiRepeatStackComposition
***********************************************************************/

			vint GuiRepeatStackComposition::GetRepeatCompositionCount()
			{
				return stackItems.Count();
			}

			GuiGraphicsComposition* GuiRepeatStackComposition::GetRepeatComposition(vint index)
			{
				return stackItems[index];
			}

			GuiGraphicsComposition* GuiRepeatStackComposition::InsertRepeatComposition(vint index)
			{
				CHECK_ERROR(0 <= index && index <= stackItems.Count(), L"GuiRepeatStackComposition::InsertRepeatComposition(vint)#Index out of range.");
				auto item = new GuiStackItemComposition;
				InsertStackItem(index, item);
				return item;
			}

			GuiGraphicsComposition* GuiRepeatStackComposition::RemoveRepeatComposition(vint index)
			{
				auto item = stackItems[index];
				RemoveChild(item);
				return item;
			}

/***********************************************************************
GuiRepeatFlowComposition
***********************************************************************/

			vint GuiRepeatFlowComposition::GetRepeatCompositionCount()
			{
				return flowItems.Count();
			}

			GuiGraphicsComposition* GuiRepeatFlowComposition::GetRepeatComposition(vint index)
			{
				return flowItems[index];
			}

			GuiGraphicsComposition* GuiRepeatFlowComposition::InsertRepeatComposition(vint index)
			{
				CHECK_ERROR(0 <= index && index <= flowItems.Count(), L"GuiRepeatStackComposition::InsertRepeatComposition(vint)#Index out of range.");
				auto item = new GuiFlowItemComposition;
				InsertFlowItem(index, item);
				return item;
			}

			GuiGraphicsComposition* GuiRepeatFlowComposition::RemoveRepeatComposition(vint index)
			{
				auto item = flowItems[index];
				RemoveChild(item);
				return item;
			}
		}
	}
}

/***********************************************************************
.\GRAPHICSCOMPOSITION\GUIGRAPHICSCOMPOSITIONBASE.CPP
***********************************************************************/

namespace vl
{
	namespace presentation
	{
		namespace compositions
		{
			using namespace collections;
			using namespace controls;
			using namespace elements;

			void InvokeOnCompositionStateChanged(compositions::GuiGraphicsComposition* composition)
			{
				composition->InvokeOnCompositionStateChanged();
			}

/***********************************************************************
GuiGraphicsComposition
***********************************************************************/

			void GuiGraphicsComposition::OnControlParentChanged(controls::GuiControl* control)
			{
				if(associatedControl && associatedControl!=control)
				{
					if(associatedControl->GetParent())
					{
						associatedControl->GetParent()->OnChildRemoved(associatedControl);
					}
					if(control)
					{
						control->OnChildInserted(associatedControl);
					}
				}
				else
				{
					for(vint i=0;i<children.Count();i++)
					{
						children[i]->OnControlParentChanged(control);
					}
				}
			}

			void GuiGraphicsComposition::OnChildInserted(GuiGraphicsComposition* child)
			{
				child->OnControlParentChanged(GetRelatedControl());
			}

			void GuiGraphicsComposition::OnChildRemoved(GuiGraphicsComposition* child)
			{
				child->OnControlParentChanged(0);
			}

			void GuiGraphicsComposition::OnParentChanged(GuiGraphicsComposition* oldParent, GuiGraphicsComposition* newParent)
			{
				OnParentLineChanged();
			}

			void GuiGraphicsComposition::OnParentLineChanged()
			{
				for (vint i = 0; i < children.Count(); i++)
				{
					children[i]->OnParentLineChanged();
				}
			}

			void GuiGraphicsComposition::OnRenderContextChanged()
			{
			}

			void GuiGraphicsComposition::UpdateRelatedHostRecord(GraphicsHostRecord* record)
			{
				relatedHostRecord = record;
				auto renderTarget = GetRenderTarget();

				if (ownedElement)
				{
					if (auto renderer = ownedElement->GetRenderer())
					{
						renderer->SetRenderTarget(renderTarget);
					}
				}

				for (vint i = 0; i < children.Count(); i++)
				{
					children[i]->UpdateRelatedHostRecord(record);
				}

				if (HasEventReceiver())
				{
					GetEventReceiver()->renderTargetChanged.Execute(GuiEventArgs(this));
				}
				if (associatedControl)
				{
					associatedControl->OnRenderTargetChanged(renderTarget);
				}

				OnRenderContextChanged();
			}

			void GuiGraphicsComposition::SetAssociatedControl(controls::GuiControl* control)
			{
				if (associatedControl)
				{
					for (vint i = 0; i < children.Count(); i++)
					{
						children[i]->OnControlParentChanged(0);
					}
				}
				associatedControl = control;
				if (associatedControl)
				{
					for (vint i = 0; i < children.Count(); i++)
					{
						children[i]->OnControlParentChanged(associatedControl);
					}
				}
			}

			void GuiGraphicsComposition::InvokeOnCompositionStateChanged()
			{
				if (relatedHostRecord)
				{
					relatedHostRecord->host->RequestRender();
				}
			}

			bool GuiGraphicsComposition::SharedPtrDestructorProc(DescriptableObject* obj, bool forceDisposing)
			{
				GuiGraphicsComposition* value=dynamic_cast<GuiGraphicsComposition*>(obj);
				if(value->parent)
				{
					if (!forceDisposing) return false;
				}
				SafeDeleteComposition(value);
				return true;
			}

			GuiGraphicsComposition::GuiGraphicsComposition()
			{
				sharedPtrDestructorProc = &GuiGraphicsComposition::SharedPtrDestructorProc;
			}

			GuiGraphicsComposition::~GuiGraphicsComposition()
			{
				for(vint i=0;i<children.Count();i++)
				{
					delete children[i];
				}
			}

			GuiGraphicsComposition* GuiGraphicsComposition::GetParent()
			{
				return parent;
			}

			const GuiGraphicsComposition::CompositionList& GuiGraphicsComposition::Children()
			{
				return children;
			}

			bool GuiGraphicsComposition::AddChild(GuiGraphicsComposition* child)
			{
				return InsertChild(children.Count(), child);
			}

			bool GuiGraphicsComposition::InsertChild(vint index, GuiGraphicsComposition* child)
			{
				if (!child) return false;
				if (child->GetParent()) return false;
				children.Insert(index, child);

				// composition parent changed -> control parent changed -> related host changed
				child->parent = this;
				child->OnParentChanged(nullptr, this);
				OnChildInserted(child);
				child->UpdateRelatedHostRecord(relatedHostRecord);

				InvokeOnCompositionStateChanged();
				return true;
			}

			bool GuiGraphicsComposition::RemoveChild(GuiGraphicsComposition* child)
			{
				if (!child) return false;
				vint index = children.IndexOf(child);
				if (index == -1) return false;

				// composition parent changed -> control parent changed -> related host changed
				child->parent = nullptr;
				child->OnParentChanged(this, nullptr);
				OnChildRemoved(child);
				child->UpdateRelatedHostRecord(nullptr);

				GuiGraphicsHost* host = GetRelatedGraphicsHost();
				if (host)
				{
					host->DisconnectComposition(child);
				}
				children.RemoveAt(index);
				InvokeOnCompositionStateChanged();
				return true;
			}

			bool GuiGraphicsComposition::MoveChild(GuiGraphicsComposition* child, vint newIndex)
			{
				if(!child) return false;
				vint index=children.IndexOf(child);
				if(index==-1) return false;
				children.RemoveAt(index);
				children.Insert(newIndex, child);
				InvokeOnCompositionStateChanged();
				return true;
			}

			Ptr<IGuiGraphicsElement> GuiGraphicsComposition::GetOwnedElement()
			{
				return ownedElement;
			}

			void GuiGraphicsComposition::SetOwnedElement(Ptr<IGuiGraphicsElement> element)
			{
				if (ownedElement != element)
				{
					if (ownedElement)
					{
						if (auto renderer = ownedElement->GetRenderer())
						{
							renderer->SetRenderTarget(nullptr);
						}
						ownedElement->SetOwnerComposition(nullptr);
					}
					ownedElement = element;
					if (ownedElement)
					{
						if (auto renderer = ownedElement->GetRenderer())
						{
							renderer->SetRenderTarget(GetRenderTarget());
						}
						ownedElement->SetOwnerComposition(this);
					}
					InvokeOnCompositionStateChanged();
				}
			}

			bool GuiGraphicsComposition::GetVisible()
			{
				return visible;
			}

			void GuiGraphicsComposition::SetVisible(bool value)
			{
				visible = value;
				InvokeOnCompositionStateChanged();
			}

			GuiGraphicsComposition::MinSizeLimitation GuiGraphicsComposition::GetMinSizeLimitation()
			{
				return minSizeLimitation;
			}

			void GuiGraphicsComposition::SetMinSizeLimitation(MinSizeLimitation value)
			{
				minSizeLimitation = value;
				InvokeOnCompositionStateChanged();
			}

			elements::IGuiGraphicsRenderTarget* GuiGraphicsComposition::GetRenderTarget()
			{
				return relatedHostRecord ? relatedHostRecord->renderTarget : nullptr;
			}

			void GuiGraphicsComposition::Render(Size offset)
			{
				auto renderTarget = GetRenderTarget();
				if (visible && renderTarget && !renderTarget->IsClipperCoverWholeTarget())
				{
					Rect bounds = GetBounds();
					bounds.x1 += margin.left;
					bounds.y1 += margin.top;
					bounds.x2 -= margin.right;
					bounds.y2 -= margin.bottom;

					if (bounds.x1 <= bounds.x2 && bounds.y1 <= bounds.y2)
					{
						bounds.x1 += offset.x;
						bounds.x2 += offset.x;
						bounds.y1 += offset.y;
						bounds.y2 += offset.y;

						if (ownedElement)
						{
							IGuiGraphicsRenderer* renderer = ownedElement->GetRenderer();
							if (renderer)
							{
								renderer->Render(bounds);
							}
						}
						if (children.Count() > 0)
						{
							bounds.x1 += internalMargin.left;
							bounds.y1 += internalMargin.top;
							bounds.x2 -= internalMargin.right;
							bounds.y2 -= internalMargin.bottom;
							if (bounds.x1 <= bounds.x2 && bounds.y1 <= bounds.y2)
							{
								offset = bounds.GetSize();
								renderTarget->PushClipper(bounds);
								if (!renderTarget->IsClipperCoverWholeTarget())
								{
									for (vint i = 0; i < children.Count(); i++)
									{
										children[i]->Render(Size(bounds.x1, bounds.y1));
									}
								}
								renderTarget->PopClipper();
							}
						}
					}
				}
			}

			GuiGraphicsEventReceiver* GuiGraphicsComposition::GetEventReceiver()
			{
				if(!eventReceiver)
				{
					eventReceiver=new GuiGraphicsEventReceiver(this);
				}
				return eventReceiver.Obj();
			}

			bool GuiGraphicsComposition::HasEventReceiver()
			{
				return eventReceiver;
			}

			GuiGraphicsComposition* GuiGraphicsComposition::FindComposition(Point location, bool forMouseEvent)
			{
				if (!visible) return 0;
				Rect bounds = GetBounds();
				Rect relativeBounds = Rect(Point(0, 0), bounds.GetSize());
				if (relativeBounds.Contains(location))
				{
					Rect clientArea = GetClientArea();
					for (vint i = children.Count() - 1; i >= 0; i--)
					{
						GuiGraphicsComposition* child = children[i];
						Rect childBounds = child->GetBounds();
						vint offsetX = childBounds.x1 + (clientArea.x1 - bounds.x1);
						vint offsetY = childBounds.y1 + (clientArea.y1 - bounds.y1);
						Point newLocation = location - Size(offsetX, offsetY);
						GuiGraphicsComposition* childResult = child->FindComposition(newLocation, forMouseEvent);
						if (childResult)
						{
							return childResult;
						}
					}

					if (!forMouseEvent || !transparentToMouse)
					{
						return this;
					}
				}
				return nullptr;
			}

			bool GuiGraphicsComposition::GetTransparentToMouse()
			{
				return transparentToMouse;
			}

			void GuiGraphicsComposition::SetTransparentToMouse(bool value)
			{
				transparentToMouse = value;
			}

			Rect GuiGraphicsComposition::GetGlobalBounds()
			{
				Rect bounds = GetBounds();
				GuiGraphicsComposition* composition = parent;
				while (composition)
				{
					Rect clientArea = composition->GetClientArea();
					Rect parentBounds = composition->GetBounds();
					bounds.x1 += clientArea.x1;
					bounds.x2 += clientArea.x1;
					bounds.y1 += clientArea.y1;
					bounds.y2 += clientArea.y1;
					composition = composition->parent;
				}
				return bounds;
			}

			controls::GuiControl* GuiGraphicsComposition::GetAssociatedControl()
			{
				return associatedControl;
			}

			GuiGraphicsHost* GuiGraphicsComposition::GetAssociatedHost()
			{
				if (relatedHostRecord && relatedHostRecord->host->GetMainComposition() == this)
				{
					return relatedHostRecord->host;
				}
				else
				{
					return nullptr;
				}
			}

			INativeCursor* GuiGraphicsComposition::GetAssociatedCursor()
			{
				return associatedCursor;
			}

			void GuiGraphicsComposition::SetAssociatedCursor(INativeCursor* cursor)
			{
				associatedCursor = cursor;
			}

			INativeWindowListener::HitTestResult GuiGraphicsComposition::GetAssociatedHitTestResult()
			{
				return associatedHitTestResult;
			}

			void GuiGraphicsComposition::SetAssociatedHitTestResult(INativeWindowListener::HitTestResult value)
			{
				associatedHitTestResult = value;
			}

			controls::GuiControl* GuiGraphicsComposition::GetRelatedControl()
			{
				GuiGraphicsComposition* composition = this;
				while (composition)
				{
					if (composition->GetAssociatedControl())
					{
						return composition->GetAssociatedControl();
					}
					else
					{
						composition = composition->GetParent();
					}
				}
				return nullptr;
			}

			GuiGraphicsHost* GuiGraphicsComposition::GetRelatedGraphicsHost()
			{
				return relatedHostRecord ? relatedHostRecord->host : nullptr;
			}

			controls::GuiControlHost* GuiGraphicsComposition::GetRelatedControlHost()
			{
				if (auto control = GetRelatedControl())
				{
					return control->GetRelatedControlHost();
				}
				return nullptr;
			}

			INativeCursor* GuiGraphicsComposition::GetRelatedCursor()
			{
				GuiGraphicsComposition* composition = this;
				while (composition)
				{
					if (composition->GetAssociatedCursor())
					{
						return composition->GetAssociatedCursor();
					}
					else
					{
						composition = composition->GetParent();
					}
				}
				return nullptr;
			}

			Margin GuiGraphicsComposition::GetMargin()
			{
				return margin;
			}

			void GuiGraphicsComposition::SetMargin(Margin value)
			{
				margin = value;
				InvokeOnCompositionStateChanged();
			}

			Margin GuiGraphicsComposition::GetInternalMargin()
			{
				return internalMargin;
			}

			void GuiGraphicsComposition::SetInternalMargin(Margin value)
			{
				internalMargin = value;
				InvokeOnCompositionStateChanged();
			}

			Size GuiGraphicsComposition::GetPreferredMinSize()
			{
				return preferredMinSize;
			}

			void GuiGraphicsComposition::SetPreferredMinSize(Size value)
			{
				preferredMinSize = value;
				InvokeOnCompositionStateChanged();
			}

			Rect GuiGraphicsComposition::GetClientArea()
			{
				Rect bounds=GetBounds();
				bounds.x1+=margin.left+internalMargin.left;
				bounds.y1+=margin.top+internalMargin.top;
				bounds.x2-=margin.right+internalMargin.right;
				bounds.y2-=margin.bottom+internalMargin.bottom;
				return bounds;
			}

			void GuiGraphicsComposition::ForceCalculateSizeImmediately()
			{
				for (vint i = 0; i < children.Count(); i++)
				{
					children[i]->ForceCalculateSizeImmediately();
				}
				InvokeOnCompositionStateChanged();
			}

/***********************************************************************
GuiGraphicsSite
***********************************************************************/

			Rect GuiGraphicsSite::GetBoundsInternal(Rect expectedBounds)
			{
				Size minSize = GetMinPreferredClientSize();
				if (minSize.x < preferredMinSize.x) minSize.x = preferredMinSize.x;
				if (minSize.y < preferredMinSize.y) minSize.y = preferredMinSize.y;

				minSize.x += margin.left + margin.right + internalMargin.left + internalMargin.right;
				minSize.y += margin.top + margin.bottom + internalMargin.top + internalMargin.bottom;
				vint w = expectedBounds.Width();
				vint h = expectedBounds.Height();
				if (minSize.x < w) minSize.x = w;
				if (minSize.y < h) minSize.y = h;
				return Rect(expectedBounds.LeftTop(), minSize);
			}

			void GuiGraphicsSite::UpdatePreviousBounds(Rect bounds)
			{
				if (previousBounds != bounds)
				{
					previousBounds = bounds;
					BoundsChanged.Execute(GuiEventArgs(this));
					InvokeOnCompositionStateChanged();
				}
			}

			GuiGraphicsSite::GuiGraphicsSite()
			{
				BoundsChanged.SetAssociatedComposition(this);
			}

			GuiGraphicsSite::~GuiGraphicsSite()
			{
			}

			bool GuiGraphicsSite::IsSizeAffectParent()
			{
				return true;
			}

			Size GuiGraphicsSite::GetMinPreferredClientSize()
			{
				Size minSize;
				if (minSizeLimitation != GuiGraphicsComposition::NoLimit)
				{
					if (ownedElement)
					{
						IGuiGraphicsRenderer* renderer = ownedElement->GetRenderer();
						if (renderer)
						{
							minSize = renderer->GetMinSize();
						}
					}
				}
				if (minSizeLimitation == GuiGraphicsComposition::LimitToElementAndChildren)
				{
					vint childCount = Children().Count();
					for (vint i = 0; i < childCount; i++)
					{
						GuiGraphicsComposition* child = children[i];
						if (child->IsSizeAffectParent())
						{
							Rect childBounds = child->GetPreferredBounds();
							if (minSize.x < childBounds.x2) minSize.x = childBounds.x2;
							if (minSize.y < childBounds.y2) minSize.y = childBounds.y2;
						}
					}
				}
				return minSize;
			}

			Rect GuiGraphicsSite::GetPreferredBounds()
			{
				return GetBoundsInternal(Rect(Point(0, 0), GetMinPreferredClientSize()));
			}

/***********************************************************************
Helper Functions
***********************************************************************/

			void NotifyFinalizeInstance(controls::GuiControl* value)
			{
				if (value)
				{
					NotifyFinalizeInstance(value->GetBoundsComposition());
				}
			}

			void NotifyFinalizeInstance(GuiGraphicsComposition* value)
			{
				if (value)
				{
					bool finalized = false;
					if (auto root = dynamic_cast<GuiInstanceRootObject*>(value))
					{
						if (root->IsFinalized())
						{
							finalized = true;
						}
						else
						{
							root->FinalizeInstance();
						}
					}

					if (auto control = value->GetAssociatedControl())
					{
						if (auto root = dynamic_cast<GuiInstanceRootObject*>(control))
						{
							if (root->IsFinalized())
							{
								finalized = true;
							}
							else
							{
								root->FinalizeInstance();
							}
						}
					}

					if (!finalized)
					{
						vint count = value->Children().Count();
						for (vint i = 0; i < count; i++)
						{
							NotifyFinalizeInstance(value->Children()[i]);
						}
					}
				}
			}

			void SafeDeleteControlInternal(controls::GuiControl* value)
			{
				if(value)
				{
					if (value->GetRelatedControlHost() != value)
					{
						GuiGraphicsComposition* bounds = value->GetBoundsComposition();
						if (bounds->GetParent())
						{
							bounds->GetParent()->RemoveChild(bounds);
						}
					}
					delete value;
				}
			}

			void SafeDeleteCompositionInternal(GuiGraphicsComposition* value)
			{
				if (value)
				{
					if (value->GetParent())
					{
						value->GetParent()->RemoveChild(value);
					}

					if (value->GetAssociatedControl())
					{
						SafeDeleteControlInternal(value->GetAssociatedControl());
					}
					else
					{
						for (vint i = value->Children().Count() - 1; i >= 0; i--)
						{
							SafeDeleteCompositionInternal(value->Children().Get(i));
						}
						delete value;
					}
				}
			}

			void SafeDeleteControl(controls::GuiControl* value)
			{
				NotifyFinalizeInstance(value);
				SafeDeleteControlInternal(value);
			}

			void SafeDeleteComposition(GuiGraphicsComposition* value)
			{
				NotifyFinalizeInstance(value);
				SafeDeleteCompositionInternal(value);
			}
		}
	}
}

/***********************************************************************
.\GRAPHICSCOMPOSITION\GUIGRAPHICSRESPONSIVECOMPOSITION.CPP
***********************************************************************/

namespace vl
{
	namespace presentation
	{
		namespace compositions
		{
			using namespace collections;
			using namespace controls;

/***********************************************************************
GuiResponsiveCompositionBase
***********************************************************************/

			void GuiResponsiveCompositionBase::OnParentLineChanged()
			{
				GuiBoundsComposition::OnParentLineChanged();
				GuiResponsiveCompositionBase* responsive = nullptr;
				{
					auto parent = GetParent();
					while (parent)
					{
						if ((responsive = dynamic_cast<GuiResponsiveCompositionBase*>(parent)))
						{
							break;
						}
						parent = parent->GetParent();
					}
				}

				if (responsiveParent != responsive)
				{
					if (responsiveParent)
					{
						responsiveParent->OnResponsiveChildRemoved(this);
						responsiveParent->OnResponsiveChildLevelUpdated();
					}
					responsiveParent = responsive;
					if (responsiveParent)
					{
						responsiveParent->OnResponsiveChildInserted(this);
						responsiveParent->OnResponsiveChildLevelUpdated();
					}
				}
			}

			void GuiResponsiveCompositionBase::OnResponsiveChildInserted(GuiResponsiveCompositionBase* child)
			{
			}

			void GuiResponsiveCompositionBase::OnResponsiveChildRemoved(GuiResponsiveCompositionBase* child)
			{
			}

			void GuiResponsiveCompositionBase::OnResponsiveChildLevelUpdated()
			{
				if (responsiveParent)
				{
					responsiveParent->OnResponsiveChildLevelUpdated();
				}
				else
				{
					InvokeOnCompositionStateChanged();
				}
			}

			GuiResponsiveCompositionBase::GuiResponsiveCompositionBase()
			{
				SetMinSizeLimitation(LimitToElementAndChildren);
				SetPreferredMinSize(Size(1, 1));

				LevelCountChanged.SetAssociatedComposition(this);
				CurrentLevelChanged.SetAssociatedComposition(this);
			}

			GuiResponsiveCompositionBase::~GuiResponsiveCompositionBase()
			{
			}

			ResponsiveDirection GuiResponsiveCompositionBase::GetDirection()
			{
				return direction;
			}

			void GuiResponsiveCompositionBase::SetDirection(ResponsiveDirection value)
			{
				if (direction != value)
				{
					direction = value;
					OnResponsiveChildLevelUpdated();
				}
			}

/***********************************************************************
GuiResponsiveSharedCollection
***********************************************************************/

			void GuiResponsiveSharedCollection::BeforeInsert(vint index, controls::GuiControl* const& value)
			{
				CHECK_ERROR(!value->GetBoundsComposition()->GetParent(), L"GuiResponsiveSharedCollection::BeforeInsert(vint, GuiResponsiveSharedCollection* const&)#Cannot insert a shared control that is currently in use.");
			}

			void GuiResponsiveSharedCollection::AfterInsert(vint index, controls::GuiControl* const& value)
			{
				view->OnResponsiveChildLevelUpdated();
			}

			void GuiResponsiveSharedCollection::BeforeRemove(vint index, controls::GuiControl* const& value)
			{
				CHECK_ERROR(!value->GetBoundsComposition()->GetParent(), L"GuiResponsiveSharedCollection::BeforeRemove(vint, GuiResponsiveSharedCollection* const&)#Cannot remove a shared control that is currently in use.");
			}

			void GuiResponsiveSharedCollection::AfterRemove(vint index, vint count)
			{
				view->OnResponsiveChildLevelUpdated();
			}

			GuiResponsiveSharedCollection::GuiResponsiveSharedCollection(GuiResponsiveViewComposition* _view)
				:view(_view)
			{
			}

			GuiResponsiveSharedCollection::~GuiResponsiveSharedCollection()
			{
			}

/***********************************************************************
GuiResponsiveViewCollection
***********************************************************************/

			void GuiResponsiveViewCollection::BeforeInsert(vint index, GuiResponsiveCompositionBase* const& value)
			{
				CHECK_ERROR(!value->GetParent(), L"GuiResponsiveViewCollection::BeforeRemove(vint, GuiResponsiveCompositionBase* const&)#Cannot insert a view that is currently in use.");
			}

			void GuiResponsiveViewCollection::AfterInsert(vint index, GuiResponsiveCompositionBase* const& value)
			{
				if (!view->currentView)
				{
					view->skipUpdatingLevels = true;
					view->currentView = value;
					view->currentView->SetAlignmentToParent(Margin(0, 0, 0, 0));
					view->AddChild(view->currentView);
					view->skipUpdatingLevels = false;
				}
				view->OnResponsiveChildLevelUpdated();
			}

			void GuiResponsiveViewCollection::BeforeRemove(vint index, GuiResponsiveCompositionBase* const& value)
			{
				CHECK_ERROR(!value->GetParent(), L"GuiResponsiveViewCollection::BeforeRemove(vint, GuiResponsiveCompositionBase* const&)#Cannot remove a view that is currently in use.");
			}

			void GuiResponsiveViewCollection::AfterRemove(vint index, vint count)
			{
				view->OnResponsiveChildLevelUpdated();
			}

			GuiResponsiveViewCollection::GuiResponsiveViewCollection(GuiResponsiveViewComposition* _view)
				:view(_view)
			{
			}

			GuiResponsiveViewCollection::~GuiResponsiveViewCollection()
			{
			}

/***********************************************************************
GuiResponsiveSharedComposition
***********************************************************************/

			void GuiResponsiveSharedComposition::SetSharedControl()
			{
				if (shared && view && !view->destructing)
				{
					auto sharedParent = shared->GetBoundsComposition()->GetParent();
					CHECK_ERROR(view->sharedControls.Contains(shared), L"GuiResponsiveSharedComposition::SetSharedControl()#The specified shared control is not in GuiResponsiveViewComposition::GetSharedControls().");
					CHECK_ERROR(!sharedParent || sharedParent == this, L"GuiResponsiveSharedComposition::SetSharedControl()#The specified shared control has not been released. This usually means this control is not in GuiResponsiveViewComposition::GetSharedControls().");

					if (!sharedParent)
					{
						shared->GetBoundsComposition()->SetAlignmentToParent(Margin(0, 0, 0, 0));
						AddChild(shared->GetBoundsComposition());
						view->usedSharedControls.Add(shared);
					}
				}
			}

			void GuiResponsiveSharedComposition::OnParentLineChanged()
			{
				GuiBoundsComposition::OnParentLineChanged();
				if (view && view->destructing)
				{
					return;
				}

				GuiResponsiveViewComposition* currentView = nullptr;
				{
					auto parent = GetParent();
					while (parent)
					{
						if ((currentView = dynamic_cast<GuiResponsiveViewComposition*>(parent)))
						{
							break;
						}
						parent = parent->GetParent();
					}
				}

				if (currentView != view && view && shared)
				{
					RemoveChild(shared->GetBoundsComposition());
					view->usedSharedControls.Remove(shared);
				}
				view = currentView;

				SetSharedControl();
			}

			GuiResponsiveSharedComposition::GuiResponsiveSharedComposition()
			{
				SetMinSizeLimitation(LimitToElementAndChildren);
			}

			GuiResponsiveSharedComposition::~GuiResponsiveSharedComposition()
			{
			}

			controls::GuiControl* GuiResponsiveSharedComposition::GetShared()
			{
				return shared;
			}

			void GuiResponsiveSharedComposition::SetShared(controls::GuiControl* value)
			{
				if (shared != value)
				{
					CHECK_ERROR(!shared || !shared->GetBoundsComposition()->GetParent(), L"GuiResponsiveSharedComposition::SetShared(GuiControl*)#Cannot replace a shared control that is currently in use.");
					shared = value;
					SetSharedControl();
				}
			}

/***********************************************************************
GuiResponsiveViewComposition
***********************************************************************/

			bool GuiResponsiveViewComposition::CalculateLevelCount()
			{
				vint old = levelCount;
				if (views.Count() == 0)
				{
					levelCount = 1;
				}
				else
				{
					levelCount = 0;
					for (vint i = 0; i < views.Count(); i++)
					{
						auto view = views[i];
						if (((vint)direction & (vint)view->GetDirection()) != 0)
						{
							levelCount += view->GetLevelCount();
						}
						else
						{
							levelCount += 1;
						}
					}
				}

				if (old != levelCount)
				{
					LevelCountChanged.Execute(GuiEventArgs(this));
					return true;
				}
				return false;
			}

			bool GuiResponsiveViewComposition::CalculateCurrentLevel()
			{
				vint old = currentLevel;
				currentLevel = 0;
				for (vint i = views.Count() - 1; i >= 0; i--)
				{
					auto view = views[i];
					if (((vint)direction & (vint)view->GetDirection()) != 0)
					{
						if (currentView == view)
						{
							currentLevel += view->GetCurrentLevel() + 1;
							break;
						}
						else
						{
							currentLevel += view->GetLevelCount();
						}
					}
					else
					{
						currentLevel++;
					}
				}
				currentLevel--;

				if (old != currentLevel)
				{
					CurrentLevelChanged.Execute(GuiEventArgs(this));
					return true;
				}
				return false;
			}

			void GuiResponsiveViewComposition::OnResponsiveChildLevelUpdated()
			{
				if (!skipUpdatingLevels)
				{
					CalculateLevelCount();
					CalculateCurrentLevel();
					GuiResponsiveCompositionBase::OnResponsiveChildLevelUpdated();
				}
			}

			GuiResponsiveViewComposition::GuiResponsiveViewComposition()
				:sharedControls(this)
				, views(this)
			{
				BeforeSwitchingView.SetAssociatedComposition(this);
			}

			GuiResponsiveViewComposition::~GuiResponsiveViewComposition()
			{
				destructing = true;

				FOREACH(GuiResponsiveCompositionBase*, view, views)
				{
					if (view != currentView)
					{
						SafeDeleteComposition(view);
					}
				}

				FOREACH(GuiControl*, shared, From(sharedControls).Except(usedSharedControls))
				{
					SafeDeleteControl(shared);
				}
			}

			vint GuiResponsiveViewComposition::GetLevelCount()
			{
				return levelCount;
			}

			vint GuiResponsiveViewComposition::GetCurrentLevel()
			{
				return currentLevel;
			}

			bool GuiResponsiveViewComposition::LevelDown()
			{
				skipUpdatingLevels = true;
				if (((vint)direction & (vint)currentView->GetDirection()) != 0 && !currentView->LevelDown())
				{
					vint index = views.IndexOf(currentView);
					if (index < views.Count() - 1)
					{
						RemoveChild(currentView);
						currentView = views[index + 1];
						currentView->SetAlignmentToParent(Margin(0, 0, 0, 0));
						{
							GuiItemEventArgs arguments(this);
							arguments.itemIndex = views.IndexOf(currentView);
							BeforeSwitchingView.Execute(arguments);
						}
						AddChild(currentView);
					}
				}
				skipUpdatingLevels = false;
				
				auto x = CalculateLevelCount();
				auto y = CalculateCurrentLevel();
				if (!x && !y) return false;
				InvokeOnCompositionStateChanged();
				return true;
			}

			bool GuiResponsiveViewComposition::LevelUp()
			{
				skipUpdatingLevels = true;
				if (((vint)direction & (vint)currentView->GetDirection()) != 0 && !currentView->LevelUp())
				{
					vint index = views.IndexOf(currentView);
					if (index > 0)
					{
						RemoveChild(currentView);
						currentView = views[index - 1];
						currentView->SetAlignmentToParent(Margin(0, 0, 0, 0));
						{
							GuiItemEventArgs arguments(this);
							arguments.itemIndex = views.IndexOf(currentView);
							BeforeSwitchingView.Execute(arguments);
						}
						AddChild(currentView);
					}
				}
				skipUpdatingLevels = false;

				auto x = CalculateLevelCount();
				auto y = CalculateCurrentLevel();
				if (!x && !y) return false;
				InvokeOnCompositionStateChanged();
				return true;
			}

			GuiResponsiveCompositionBase* GuiResponsiveViewComposition::GetCurrentView()
			{
				return currentView;
			}

			collections::ObservableListBase<controls::GuiControl*>& GuiResponsiveViewComposition::GetSharedControls()
			{
				return sharedControls;
			}

			collections::ObservableListBase<GuiResponsiveCompositionBase*>& GuiResponsiveViewComposition::GetViews()
			{
				return views;
			}

/***********************************************************************
GuiResponsiveFixedComposition
***********************************************************************/

			void GuiResponsiveFixedComposition::OnResponsiveChildLevelUpdated()
			{
				InvokeOnCompositionStateChanged();
			}

			GuiResponsiveFixedComposition::GuiResponsiveFixedComposition()
			{
			}

			GuiResponsiveFixedComposition::~GuiResponsiveFixedComposition()
			{
			}

			vint GuiResponsiveFixedComposition::GetLevelCount()
			{
				return 1;
			}

			vint GuiResponsiveFixedComposition::GetCurrentLevel()
			{
				return 0;
			}

			bool GuiResponsiveFixedComposition::LevelDown()
			{
				return false;
			}

			bool GuiResponsiveFixedComposition::LevelUp()
			{
				return false;
			}

/***********************************************************************
GuiResponsiveStackComposition
***********************************************************************/

#define DEFINE_AVAILABLE \
			auto availables = From(responsiveChildren) \
				.Where([=](GuiResponsiveCompositionBase* child) \
				{ \
					return ((vint)direction & (vint)child->GetDirection()) != 0; \
				}) \

			bool GuiResponsiveStackComposition::CalculateLevelCount()
			{
				vint old = levelCount;
				DEFINE_AVAILABLE;
				if (availables.IsEmpty())
				{
					levelCount = 1;
				}
				else
				{
					levelCount = availables
						.Select([](GuiResponsiveCompositionBase* child)
						{
							return child->GetLevelCount() - 1;
						})
						.Aggregate([](vint a, vint b)
						{
							return a + b;
						}) + 1;
				}

				if (old != levelCount)
				{
					LevelCountChanged.Execute(GuiEventArgs(this));
					return true;
				}
				return false;
			}

			bool GuiResponsiveStackComposition::CalculateCurrentLevel()
			{
				vint old = currentLevel;
				DEFINE_AVAILABLE;
				if (availables.IsEmpty())
				{
					currentLevel = 0;
				}
				else
				{
					currentLevel = availables
						.Select([](GuiResponsiveCompositionBase* child)
						{
							return child->GetCurrentLevel();
						})
						.Aggregate([](vint a, vint b)
						{
							return a + b;
						});
				}

				if (old != currentLevel)
				{
					CurrentLevelChanged.Execute(GuiEventArgs(this));
					return true;
				}
				return false;
			}

			void GuiResponsiveStackComposition::OnResponsiveChildInserted(GuiResponsiveCompositionBase* child)
			{
				responsiveChildren.Add(child);
			}

			void GuiResponsiveStackComposition::OnResponsiveChildRemoved(GuiResponsiveCompositionBase* child)
			{
				responsiveChildren.Remove(child);
			}

			void GuiResponsiveStackComposition::OnResponsiveChildLevelUpdated()
			{
				CalculateLevelCount();
				CalculateCurrentLevel();
				GuiResponsiveCompositionBase::OnResponsiveChildLevelUpdated();
			}

			bool GuiResponsiveStackComposition::ChangeLevel(bool levelDown)
			{
				DEFINE_AVAILABLE;

				SortedList<GuiResponsiveCompositionBase*> ignored;
				while (true)
				{
					GuiResponsiveCompositionBase* selected = nullptr;
					vint size = 0;

					FOREACH(GuiResponsiveCompositionBase*, child, availables)
					{
						if (!ignored.Contains(child))
						{
							Size childSize = child->GetPreferredBounds().GetSize();
							vint childSizeToCompare =
								direction == ResponsiveDirection::Horizontal ? childSize.x :
								direction == ResponsiveDirection::Vertical ? childSize.y :
								childSize.x * childSize.y;

							if (!selected || (levelDown ? size < childSizeToCompare : size > childSizeToCompare))
							{
								selected = child;
								size = childSizeToCompare;
							}
						}
					}

					if (!selected)
					{
						break;
					}
					else if (levelDown ? selected->LevelDown() : selected->LevelUp())
					{
						break;
					}
					else
					{
						ignored.Add(selected);
					}
				}

				if (!CalculateCurrentLevel()) return false;
				InvokeOnCompositionStateChanged();
				return true;
			}

			GuiResponsiveStackComposition::GuiResponsiveStackComposition()
			{
			}

			GuiResponsiveStackComposition::~GuiResponsiveStackComposition()
			{
			}

			vint GuiResponsiveStackComposition::GetLevelCount()
			{
				return levelCount;
			}

			vint GuiResponsiveStackComposition::GetCurrentLevel()
			{
				return currentLevel;
			}

			bool GuiResponsiveStackComposition::LevelDown()
			{
				return ChangeLevel(true);
			}

			bool GuiResponsiveStackComposition::LevelUp()
			{
				return ChangeLevel(false);
			}

/***********************************************************************
GuiResponsiveGroupComposition
***********************************************************************/

			bool GuiResponsiveGroupComposition::CalculateLevelCount()
			{
				vint old = levelCount;
				DEFINE_AVAILABLE;
				if (availables.IsEmpty())
				{
					levelCount = 1;
				}
				else
				{
					levelCount = availables
						.Select([](GuiResponsiveCompositionBase* child)
						{
							return child->GetLevelCount();
						})
						.Max();
				}

				if (old != levelCount)
				{
					LevelCountChanged.Execute(GuiEventArgs(this));
					return true;
				}
				return false;
			}

			bool GuiResponsiveGroupComposition::CalculateCurrentLevel()
			{
				vint old = currentLevel;
				DEFINE_AVAILABLE;
				if (availables.IsEmpty())
				{
					currentLevel = 0;
				}
				else
				{
					currentLevel = availables
						.Select([](GuiResponsiveCompositionBase* child)
						{
							return child->GetCurrentLevel();
						})
						.Max();
				}

				if (old != currentLevel)
				{
					CurrentLevelChanged.Execute(GuiEventArgs(this));
					return true;
				}
				return false;
			}

			void GuiResponsiveGroupComposition::OnResponsiveChildInserted(GuiResponsiveCompositionBase* child)
			{
				responsiveChildren.Add(child);
			}

			void GuiResponsiveGroupComposition::OnResponsiveChildRemoved(GuiResponsiveCompositionBase* child)
			{
				responsiveChildren.Remove(child);
			}

			void GuiResponsiveGroupComposition::OnResponsiveChildLevelUpdated()
			{
				CalculateLevelCount();
				CalculateCurrentLevel();
				GuiResponsiveCompositionBase::OnResponsiveChildLevelUpdated();
			}

			GuiResponsiveGroupComposition::GuiResponsiveGroupComposition()
			{
			}

			GuiResponsiveGroupComposition::~GuiResponsiveGroupComposition()
			{
			}

			vint GuiResponsiveGroupComposition::GetLevelCount()
			{
				return levelCount;
			}

			vint GuiResponsiveGroupComposition::GetCurrentLevel()
			{
				return currentLevel;
			}

			bool GuiResponsiveGroupComposition::LevelDown()
			{
				DEFINE_AVAILABLE;
				vint level = currentLevel;
				FOREACH(GuiResponsiveCompositionBase*, child, availables)
				{
					if (child->GetCurrentLevel() >= level)
					{
						if (!child->LevelDown())
						{
							break;
						}
					}
				}

				if (!CalculateCurrentLevel()) return false;
				InvokeOnCompositionStateChanged();
				return true;
			}

			bool GuiResponsiveGroupComposition::LevelUp()
			{
				DEFINE_AVAILABLE;
				vint level = currentLevel;
				FOREACH(GuiResponsiveCompositionBase*, child, availables)
				{
					while (child->GetCurrentLevel() <= level)
					{
						if (!child->LevelUp())
						{
							break;
						}
					}
				}

				if (!CalculateCurrentLevel()) return false;
				InvokeOnCompositionStateChanged();
				return true;
			}

#undef DEFINE_AVAILABLE

/***********************************************************************
GuiResponsiveContainerComposition
***********************************************************************/

#define RESPONSIVE_INVALID_SIZE Size(-1, -1)

			void GuiResponsiveContainerComposition::OnBoundsChanged(GuiGraphicsComposition* sender, GuiEventArgs& arguments)
			{
				if (!responsiveTarget) return;
				const Size containerSize = GetBounds().GetSize();
				const Size responsiveOriginalSize = responsiveTarget->GetPreferredBounds().GetSize();
				const bool testX = (vint)responsiveTarget->GetDirection() & (vint)ResponsiveDirection::Horizontal;
				const bool testY = (vint)responsiveTarget->GetDirection() & (vint)ResponsiveDirection::Vertical;

#define RESPONSIVE_IF_CONTAINER(OP, SIZE) ((testX && (containerSize).x OP SIZE.x) || (testY && (containerSize).y OP SIZE.y))

				if (upperLevelSize != RESPONSIVE_INVALID_SIZE && RESPONSIVE_IF_CONTAINER(>=, upperLevelSize))
				{
					upperLevelSize = RESPONSIVE_INVALID_SIZE;
				}

				if (upperLevelSize == RESPONSIVE_INVALID_SIZE && RESPONSIVE_IF_CONTAINER(>=, responsiveOriginalSize))
				{
					while (true)
					{
						if (responsiveTarget->GetCurrentLevel() == responsiveTarget->GetLevelCount() - 1)
						{
							break;
						}
						else if (responsiveTarget->LevelUp())
						{
							responsiveTarget->ForceCalculateSizeImmediately();
							auto currentSize = responsiveTarget->GetPreferredBounds().GetSize();
							if (RESPONSIVE_IF_CONTAINER(<, currentSize))
							{
								upperLevelSize = currentSize;
								responsiveTarget->LevelDown();
								break;
							}
						}
						else
						{
							break;
						}
					}
				}
				else
				{
					while (true)
					{
						responsiveTarget->ForceCalculateSizeImmediately();
						auto currentSize = responsiveTarget->GetPreferredBounds().GetSize();
						if (RESPONSIVE_IF_CONTAINER(>=, currentSize))
						{
							break;
						}

						if (responsiveTarget->GetCurrentLevel() == 0)
						{
							break;
						}
						else if(responsiveTarget->LevelDown())
						{
							upperLevelSize = currentSize;
						}
						else
						{
							break;
						}
					}
				}

#undef RESPONSIVE_IF_CONTAINER
			}

			GuiResponsiveContainerComposition::GuiResponsiveContainerComposition()
				:upperLevelSize(RESPONSIVE_INVALID_SIZE)
			{
				BoundsChanged.AttachMethod(this, &GuiResponsiveContainerComposition::OnBoundsChanged);
			}

			GuiResponsiveContainerComposition::~GuiResponsiveContainerComposition()
			{
			}

			GuiResponsiveCompositionBase* GuiResponsiveContainerComposition::GetResponsiveTarget()
			{
				return responsiveTarget;
			}

			void GuiResponsiveContainerComposition::SetResponsiveTarget(GuiResponsiveCompositionBase* value)
			{
				if (responsiveTarget != value)
				{
					if (responsiveTarget)
					{
						RemoveChild(responsiveTarget);
					}

					responsiveTarget = value;
					upperLevelSize = RESPONSIVE_INVALID_SIZE;

					if (responsiveTarget)
					{
						responsiveTarget->SetAlignmentToParent(Margin(0, 0, 0, 0));
						while (responsiveTarget->LevelUp());
						AddChild(responsiveTarget);

						GuiEventArgs arguments(this);
						OnBoundsChanged(this, arguments);
					}
				}
			}

#undef RESPONSIVE_INVALID_SIZE
		}
	}
}


/***********************************************************************
.\CONTROLS\GUIAPPLICATION.CPP
***********************************************************************/

extern void GuiMain();

namespace vl
{
	namespace presentation
	{
		namespace controls
		{
			using namespace collections;
			using namespace compositions;
			using namespace theme;
			using namespace description;

/***********************************************************************
GuiApplication
***********************************************************************/

			void GuiApplication::InvokeClipboardNotify(compositions::GuiGraphicsComposition* composition, compositions::GuiEventArgs& arguments)
			{
				if(composition->HasEventReceiver())
				{
					composition->GetEventReceiver()->clipboardNotify.Execute(arguments);
				}
				FOREACH(GuiGraphicsComposition*, subComposition, composition->Children())
				{
					InvokeClipboardNotify(subComposition, arguments);
				}
			}

			void GuiApplication::LeftButtonDown(Point position)
			{
				OnMouseDown(position);
			}

			void GuiApplication::LeftButtonUp(Point position)
			{
			}

			void GuiApplication::RightButtonDown(Point position)
			{
				OnMouseDown(position);
			}

			void GuiApplication::RightButtonUp(Point position)
			{
			}

			void GuiApplication::ClipboardUpdated()
			{
				for(vint i=0;i<windows.Count();i++)
				{
					GuiEventArgs arguments=windows[i]->GetNotifyEventArguments();
					windows[i]->ClipboardUpdated.Execute(arguments);
					InvokeClipboardNotify(windows[i]->GetBoundsComposition(), arguments);
				}
			}

			GuiApplication::GuiApplication()
				:locale(Locale::UserDefault())
			{
				GetCurrentController()->CallbackService()->InstallListener(this);
			}

			GuiApplication::~GuiApplication()
			{
				if(sharedTooltipControl)
				{
					delete sharedTooltipControl;
					sharedTooltipControl=0;
				}
				GetCurrentController()->CallbackService()->UninstallListener(this);
			}

			INativeWindow* GuiApplication::GetThreadContextNativeWindow(GuiControlHost* controlHost)
			{
				return nullptr;
			}

			void GuiApplication::RegisterWindow(GuiWindow* window)
			{
				windows.Add(window);
			}

			void GuiApplication::UnregisterWindow(GuiWindow* window)
			{
				windows.Remove(window);
			}

			void GuiApplication::RegisterPopupOpened(GuiPopup* popup)
			{
				vint index=openingPopups.IndexOf(popup);
				if(index==-1)
				{
					openingPopups.Add(popup);
					if(openingPopups.Count()==1)
					{
						GetCurrentController()->InputService()->StartHookMouse();
					}
				}
			}

			void GuiApplication::RegisterPopupClosed(GuiPopup* popup)
			{
				if(openingPopups.Remove(popup))
				{
					if(openingPopups.Count()==0)
					{
						GetCurrentController()->InputService()->StopHookMouse();
					}
				}
			}

			void GuiApplication::OnMouseDown(Point location)
			{
				GuiWindow* window=GetWindow(location);
				for(vint i=0;i<windows.Count();i++)
				{
					if(windows[i]!=window)
					{
						windows[i]->MouseClickedOnOtherWindow(window);
					}
				}
			}

			void GuiApplication::TooltipMouseEnter(compositions::GuiGraphicsComposition* sender, compositions::GuiEventArgs& arguments)
			{
				sharedTooltipHovering=true;
			}

			void GuiApplication::TooltipMouseLeave(compositions::GuiGraphicsComposition* sender, compositions::GuiEventArgs& arguments)
			{
				sharedTooltipHovering=false;
				if(sharedTooltipClosing)
				{
					CloseTooltip();
				}
			}

			Locale GuiApplication::GetLocale()
			{
				return locale;
			}

			void GuiApplication::SetLocale(Locale value)
			{
				if (locale != value)
				{
					locale = value;
					LocaleChanged();
				}
			}

			void GuiApplication::Run(GuiWindow* _mainWindow)
			{
				if (!mainWindow)
				{
					mainWindow = _mainWindow;
					GetCurrentController()->WindowService()->Run(mainWindow->GetNativeWindow());
					mainWindow = nullptr;
				}
			}

			GuiWindow* GuiApplication::GetMainWindow()
			{
				return mainWindow;
			}

			const collections::List<GuiWindow*>& GuiApplication::GetWindows()
			{
				return windows;
			}

			GuiWindow* GuiApplication::GetWindow(Point location)
			{
				INativeWindow* nativeWindow = GetCurrentController()->WindowService()->GetWindow(location);
				if (nativeWindow)
				{
					for (vint i = 0; i < windows.Count(); i++)
					{
						GuiWindow* window = windows[i];
						if (window->GetNativeWindow() == nativeWindow)
						{
							return window;
						}
					}
				}
				return 0;
			}

			void GuiApplication::ShowTooltip(GuiControl* owner, GuiControl* tooltip, vint preferredContentWidth, Point location)
			{
				GuiWindow* ownerWindow = dynamic_cast<GuiWindow*>(owner->GetRelatedControlHost());
				if (sharedTooltipOwnerWindow != ownerWindow)
				{
					delete sharedTooltipControl;
					sharedTooltipControl = 0;
				}

				if(!sharedTooltipControl)
				{
					sharedTooltipControl = new GuiTooltip(theme::ThemeName::Tooltip);
					if (ownerWindow)
					{
						if (auto tooltipStyle = ownerWindow->GetControlTemplateObject(true)->GetTooltipTemplate())
						{
							sharedTooltipControl->SetControlTemplate(tooltipStyle);
						}
					}
					sharedTooltipControl->GetBoundsComposition()->GetEventReceiver()->mouseEnter.AttachMethod(this, &GuiApplication::TooltipMouseEnter);
					sharedTooltipControl->GetBoundsComposition()->GetEventReceiver()->mouseLeave.AttachMethod(this, &GuiApplication::TooltipMouseLeave);
				}

				sharedTooltipHovering=false;
				sharedTooltipClosing=false;
				sharedTooltipOwnerWindow = ownerWindow;
				sharedTooltipOwner=owner;
				sharedTooltipControl->SetTemporaryContentControl(tooltip);
				sharedTooltipControl->SetPreferredContentWidth(preferredContentWidth);
				sharedTooltipControl->SetClientSize(Size(10, 10));
				sharedTooltipControl->ShowPopup(owner, location);
			}

			void GuiApplication::CloseTooltip()
			{
				if(sharedTooltipControl)
				{
					if(sharedTooltipHovering)
					{
						sharedTooltipClosing=true;
					}
					else
					{
						sharedTooltipClosing=false;
						sharedTooltipControl->Close();
					}
				}
			}

			GuiControl* GuiApplication::GetTooltipOwner()
			{
				if(!sharedTooltipControl) return 0;
				if(!sharedTooltipControl->GetTemporaryContentControl()) return 0;
				return sharedTooltipOwner;
			}

			WString GuiApplication::GetExecutablePath()
			{
				return GetCurrentController()->GetExecutablePath();
			}

			WString GuiApplication::GetExecutableFolder()
			{
				WString path=GetExecutablePath();
				for(vint i=path.Length()-1;i>=0;i--)
				{
					if(path[i]==L'\\' || path[i]==L'/')
					{
						return path.Sub(0, i+1);
					}
				}
				return L"";
			}

			bool GuiApplication::IsInMainThread(GuiControlHost* controlHost)
			{
				return GetCurrentController()->AsyncService()->IsInMainThread(GetThreadContextNativeWindow(controlHost));
			}

			void GuiApplication::InvokeAsync(const Func<void()>& proc)
			{
				GetCurrentController()->AsyncService()->InvokeAsync(proc);
			}

			void GuiApplication::InvokeInMainThread(GuiControlHost* controlHost, const Func<void()>& proc)
			{
				GetCurrentController()->AsyncService()->InvokeInMainThread(GetThreadContextNativeWindow(controlHost), proc);
			}

			bool GuiApplication::InvokeInMainThreadAndWait(GuiControlHost* controlHost, const Func<void()>& proc, vint milliseconds)
			{
				CHECK_ERROR(!IsInMainThread(controlHost), L"GuiApplication::InvokeInMainThreadAndWait(GuiControlHost*, const Func<void()>&, vint)#This function cannot be called in UI thread.");
				return GetCurrentController()->AsyncService()->InvokeInMainThreadAndWait(GetThreadContextNativeWindow(controlHost), proc, milliseconds);
			}

			Ptr<INativeDelay> GuiApplication::DelayExecute(const Func<void()>& proc, vint milliseconds)
			{
				return GetCurrentController()->AsyncService()->DelayExecute(proc, milliseconds);
			}

			Ptr<INativeDelay> GuiApplication::DelayExecuteInMainThread(const Func<void()>& proc, vint milliseconds)
			{
				return GetCurrentController()->AsyncService()->DelayExecuteInMainThread(proc, milliseconds);
			}

			void GuiApplication::RunGuiTask(GuiControlHost* controlHost, const Func<void()>& proc)
			{
				if(IsInMainThread(controlHost))
				{
					return proc();
				}
				else
				{
					InvokeInMainThreadAndWait(controlHost, [&proc]()
					{
						proc();
					});
				}
			}

/***********************************************************************
GuiPluginManager
***********************************************************************/

			class GuiPluginManager : public Object, public IGuiPluginManager
			{
			protected:
				List<Ptr<IGuiPlugin>>				plugins;
				bool								loaded;
			public:
				GuiPluginManager()
					:loaded(false)
				{
				}

				~GuiPluginManager()
				{
					Unload();
				}

				void AddPlugin(Ptr<IGuiPlugin> plugin)override
				{
					CHECK_ERROR(!loaded, L"GuiPluginManager::AddPlugin(Ptr<IGuiPlugin>)#Load function has already been executed.");
					auto name = plugin->GetName();
					if (name != L"")
					{
						FOREACH(Ptr<IGuiPlugin>, plugin, plugins)
						{
							CHECK_ERROR(plugin->GetName() != name, L"GuiPluginManager::AddPlugin(Ptr<IGuiPlugin>)#Duplicated plugin name.");
						}
					}
					plugins.Add(plugin);
				}

				void Load()override
				{
					CHECK_ERROR(!loaded, L"GuiPluginManager::AddPlugin(Ptr<IGuiPlugin>)#Load function has already been executed.");
					loaded=true;

					SortedList<WString> loaded;
					Group<WString, WString> loading;
					Dictionary<WString, Ptr<IGuiPlugin>> pluginsToLoad;
					FOREACH(Ptr<IGuiPlugin>, plugin, plugins)
					{
						auto name = plugin->GetName();
						pluginsToLoad.Add(name, plugin);
						List<WString> dependencies;
						plugin->GetDependencies(dependencies);
						FOREACH(WString, dependency, dependencies)
						{
							loading.Add(name, dependency);
						}
					}

					while (pluginsToLoad.Count() > 0)
					{
						vint count = pluginsToLoad.Count();
						{
							FOREACH_INDEXER(WString, name, index, pluginsToLoad.Keys())
							{
								if (!loading.Keys().Contains(name))
								{
									for (vint i = loading.Count() - 1; i >= 0; i--)
									{
										loading.Remove(loading.Keys()[i], name);
									}
									loaded.Add(name);

									auto plugin = pluginsToLoad.Values()[index];
									pluginsToLoad.Remove(name);
									plugin->Load();
									break;
								}
							}
						}
						if (count == pluginsToLoad.Count())
						{
							WString message;
							FOREACH(Ptr<IGuiPlugin>, plugin, pluginsToLoad.Values())
							{
								message += L"Cannot load plugin \"" + plugin->GetName() + L"\" because part of its dependencies are not ready:";
								List<WString> dependencies;
								plugin->GetDependencies(dependencies);
								bool first = true;
								FOREACH(WString, dependency, dependencies)
								{
									if (!loaded.Contains(dependency))
									{
										message += L" \"" + dependency + L"\";";
									}
								}
								message += L"\r\n";
							}
							throw Exception(message);
						}
					}
				}

				void Unload()override
				{
					CHECK_ERROR(loaded, L"GuiPluginManager::AddPlugin(Ptr<IGuiPlugin>)#Load function has not been executed.");
					loaded=false;
					FOREACH(Ptr<IGuiPlugin>, plugin, plugins)
					{
						plugin->Unload();
					}
				}

				bool IsLoaded()override
				{
					return loaded;
				}
			};

/***********************************************************************
Helpers
***********************************************************************/

			GuiApplication* application=0;
			IGuiPluginManager* pluginManager=0;

			GuiApplication* GetApplication()
			{
				return application;
			}

			IGuiPluginManager* GetPluginManager()
			{
				if(!pluginManager)
				{
					pluginManager=new GuiPluginManager;
				}
				return pluginManager;
			}

			void DestroyPluginManager()
			{
				if(pluginManager)
				{
					delete pluginManager;
					pluginManager=0;
				}
			}

/***********************************************************************
GuiApplicationMain
***********************************************************************/

			class UIThreadAsyncScheduler :public Object, public IAsyncScheduler, public Description<UIThreadAsyncScheduler>
			{
			public:
				void Execute(const Func<void()>& callback)override
				{
					GetApplication()->InvokeInMainThread(GetApplication()->GetMainWindow(), callback);
				}

				void ExecuteInBackground(const Func<void()>& callback)override
				{
					GetApplication()->InvokeAsync(callback);
				}

				void DelayExecute(const Func<void()>& callback, vint milliseconds)override
				{
					GetApplication()->DelayExecuteInMainThread(callback, milliseconds);
				}
			};

			class OtherThreadAsyncScheduler :public Object, public IAsyncScheduler, public Description<UIThreadAsyncScheduler>
			{
			public:
				void Execute(const Func<void()>& callback)override
				{
					GetApplication()->InvokeAsync(callback);
				}

				void ExecuteInBackground(const Func<void()>& callback)override
				{
					GetApplication()->InvokeAsync(callback);
				}

				void DelayExecute(const Func<void()>& callback, vint milliseconds)override
				{
					GetApplication()->DelayExecute(callback, milliseconds);
				}
			};

			void GuiApplicationInitialize()
			{
				GetCurrentController()->InputService()->StartTimer();
				theme::InitializeTheme();

#ifndef VCZH_DEBUG_NO_REFLECTION
				GetGlobalTypeManager()->Load();
#endif
				GetPluginManager()->Load();

				{
					GuiApplication app;
					application = &app;
					IAsyncScheduler::RegisterSchedulerForCurrentThread(new UIThreadAsyncScheduler);
					IAsyncScheduler::RegisterDefaultScheduler(new OtherThreadAsyncScheduler);
					GuiMain();
					IAsyncScheduler::UnregisterDefaultScheduler();
					IAsyncScheduler::UnregisterSchedulerForCurrentThread();
				}
				application = nullptr;

				DestroyPluginManager();
				theme::FinalizeTheme();
				ThreadLocalStorage::DisposeStorages();
				FinalizeGlobalStorage();
#ifndef VCZH_DEBUG_NO_REFLECTION
				DestroyGlobalTypeManager();
#endif
			}
		}
	}
}

void GuiApplicationMain()
{
	vl::presentation::controls::GuiApplicationInitialize();
}

/***********************************************************************
.\CONTROLS\GUIBASICCONTROLS.CPP
***********************************************************************/

namespace vl
{
	namespace presentation
	{
		namespace controls
		{
			using namespace elements;
			using namespace compositions;
			using namespace collections;
			using namespace reflection::description;

/***********************************************************************
GuiControl
***********************************************************************/

			void GuiControl::BeforeControlTemplateUninstalled()
			{
			}

			void GuiControl::AfterControlTemplateInstalled(bool initialize)
			{
				controlTemplateObject->SetText(text);
				controlTemplateObject->SetFont(font);
				controlTemplateObject->SetContext(context);
				controlTemplateObject->SetVisuallyEnabled(isVisuallyEnabled);
				controlTemplateObject->SetFocusableComposition(focusableComposition);
			}

			void GuiControl::CheckAndStoreControlTemplate(templates::GuiControlTemplate* value)
			{
				controlTemplateObject = value;
			}

			void GuiControl::EnsureControlTemplateExists()
			{
				if (!controlTemplateObject)
				{
					RebuildControlTemplate();
				}
			}

			void GuiControl::RebuildControlTemplate()
			{
				bool initialize = controlTemplateObject == nullptr;
				if (controlTemplateObject)
				{
					BeforeControlTemplateUninstalled();
					containerComposition->GetParent()->RemoveChild(containerComposition);
					boundsComposition->AddChild(containerComposition);
					SafeDeleteComposition(controlTemplateObject);
					controlTemplateObject = nullptr;
				}

				if (controlTemplate)
				{
					CheckAndStoreControlTemplate(controlTemplate({}));
				}
				else
				{
					CheckAndStoreControlTemplate(theme::GetCurrentTheme()->CreateStyle(controlThemeName)({}));
				}

				if (controlTemplateObject)
				{
					controlTemplateObject->SetAlignmentToParent(Margin(0, 0, 0, 0));

					containerComposition->GetParent()->RemoveChild(containerComposition);
					boundsComposition->AddChild(controlTemplateObject);
					controlTemplateObject->GetContainerComposition()->AddChild(containerComposition);
					AfterControlTemplateInstalled(initialize);
				}
			}

			void GuiControl::OnChildInserted(GuiControl* control)
			{
				GuiControl* oldParent=control->parent;
				children.Add(control);
				control->parent=this;
				control->OnParentChanged(oldParent, control->parent);
				control->UpdateVisuallyEnabled();
			}

			void GuiControl::OnChildRemoved(GuiControl* control)
			{
				GuiControl* oldParent=control->parent;
				control->parent=0;
				children.Remove(control);
				control->OnParentChanged(oldParent, control->parent);
			}

			void GuiControl::OnParentChanged(GuiControl* oldParent, GuiControl* newParent)
			{
				OnParentLineChanged();
			}

			void GuiControl::OnParentLineChanged()
			{
				for(vint i=0;i<children.Count();i++)
				{
					children[i]->OnParentLineChanged();
				}
			}

			void GuiControl::OnRenderTargetChanged(elements::IGuiGraphicsRenderTarget* renderTarget)
			{
				RenderTargetChanged.Execute(GetNotifyEventArguments());
			}

			void GuiControl::OnBeforeReleaseGraphicsHost()
			{
				for(vint i=0;i<children.Count();i++)
				{
					children[i]->OnBeforeReleaseGraphicsHost();
				}
			}

			void GuiControl::UpdateVisuallyEnabled()
			{
				bool newValue = isEnabled && (parent == 0 ? true : parent->GetVisuallyEnabled());
				if (isVisuallyEnabled != newValue)
				{
					isVisuallyEnabled = newValue;
					if (controlTemplateObject)
					{
						controlTemplateObject->SetVisuallyEnabled(isVisuallyEnabled);
					}
					VisuallyEnabledChanged.Execute(GetNotifyEventArguments());

					for (vint i = 0; i < children.Count(); i++)
					{
						children[i]->UpdateVisuallyEnabled();
					}
				}
			}

			void GuiControl::SetFocusableComposition(compositions::GuiGraphicsComposition* value)
			{
				if (focusableComposition != value)
				{
					focusableComposition = value;
					if (controlTemplateObject)
					{
						controlTemplateObject->SetFocusableComposition(focusableComposition);
					}
				}
			}

			bool GuiControl::IsAltEnabled()
			{
				GuiControl* control = this;
				while (control)
				{
					if (!control->GetVisible() || !control->GetEnabled())
					{
						return false;
					}
					control = control->GetParent();
				}

				return true;
			}

			bool GuiControl::IsAltAvailable()
			{
				return focusableComposition != 0 && alt != L"";
			}

			compositions::GuiGraphicsComposition* GuiControl::GetAltComposition()
			{
				return boundsComposition;
			}

			compositions::IGuiAltActionHost* GuiControl::GetActivatingAltHost()
			{
				return activatingAltHost;
			}

			void GuiControl::OnActiveAlt()
			{
				SetFocus();
			}

			bool GuiControl::SharedPtrDestructorProc(DescriptableObject* obj, bool forceDisposing)
			{
				GuiControl* value=dynamic_cast<GuiControl*>(obj);
				if(value->GetBoundsComposition()->GetParent())
				{
					if (!forceDisposing) return false;
				}
				SafeDeleteControl(value);
				return true;
			}

			GuiControl::GuiControl(theme::ThemeName themeName)
				:controlThemeName(themeName)
			{
				{
					boundsComposition = new GuiBoundsComposition;
					boundsComposition->SetAssociatedControl(this);
					boundsComposition->SetMinSizeLimitation(GuiGraphicsComposition::LimitToElementAndChildren);

					containerComposition = new GuiBoundsComposition;
					containerComposition->SetTransparentToMouse(true);
					containerComposition->SetMinSizeLimitation(GuiGraphicsComposition::LimitToElementAndChildren);
					containerComposition->SetAlignmentToParent(Margin(0, 0, 0, 0));

					boundsComposition->AddChild(containerComposition);
				}
				{
					ControlThemeNameChanged.SetAssociatedComposition(boundsComposition);
					ControlTemplateChanged.SetAssociatedComposition(boundsComposition);
					RenderTargetChanged.SetAssociatedComposition(boundsComposition);
					VisibleChanged.SetAssociatedComposition(boundsComposition);
					EnabledChanged.SetAssociatedComposition(boundsComposition);
					VisuallyEnabledChanged.SetAssociatedComposition(boundsComposition);
					AltChanged.SetAssociatedComposition(boundsComposition);
					TextChanged.SetAssociatedComposition(boundsComposition);
					FontChanged.SetAssociatedComposition(boundsComposition);
					ContextChanged.SetAssociatedComposition(boundsComposition);
				}
				font = GetCurrentController()->ResourceService()->GetDefaultFont();
				sharedPtrDestructorProc = &GuiControl::SharedPtrDestructorProc;
			}

			GuiControl::~GuiControl()
			{
				// prevent a root bounds composition from notifying its dead controls
				if (!parent)
				{
					NotifyFinalizeInstance(boundsComposition);
				}

				if (tooltipControl)
				{
					// the only legal parent is the GuiApplication::sharedTooltipWindow
					if (tooltipControl->GetBoundsComposition()->GetParent())
					{
						tooltipControl->GetBoundsComposition()->GetParent()->RemoveChild(tooltipControl->GetBoundsComposition());
					}
					delete tooltipControl;
				}

				for (vint i = 0; i < children.Count(); i++)
				{
					delete children[i];
				}
				children.Clear();

				// let the root control of a control tree delete the whole composition tree
				if (!parent)
				{
					delete boundsComposition;
				}
			}

			compositions::GuiEventArgs GuiControl::GetNotifyEventArguments()
			{
				return GuiEventArgs(boundsComposition);
			}

			theme::ThemeName GuiControl::GetControlThemeName()
			{
				return controlThemeName;
			}

			void GuiControl::SetControlThemeName(theme::ThemeName value)
			{
				SetControlThemeNameAndTemplate(value, controlTemplate);
			}

			GuiControl::ControlTemplatePropertyType GuiControl::GetControlTemplate()
			{
				return controlTemplate;
			}

			void GuiControl::SetControlTemplate(const ControlTemplatePropertyType& value)
			{
				SetControlThemeNameAndTemplate(controlThemeName, value);
			}

			void GuiControl::SetControlThemeNameAndTemplate(theme::ThemeName themeNameValue, const ControlTemplatePropertyType& controlTemplateValue)
			{
				bool themeChanged = (controlThemeName != themeNameValue);
				bool templateChanged = (controlTemplate || controlTemplateValue);
				if (themeChanged || templateChanged)
				{
					controlThemeName = themeNameValue;
					controlTemplate = controlTemplateValue;
					RebuildControlTemplate();
					
					if (themeChanged)
					{
						ControlThemeNameChanged.Execute(GetNotifyEventArguments());
					}
					if (templateChanged)
					{
						ControlTemplateChanged.Execute(GetNotifyEventArguments());
					}
				}
			}

			templates::GuiControlTemplate* GuiControl::GetControlTemplateObject()
			{
				EnsureControlTemplateExists();
				return controlTemplateObject;
			}

			compositions::GuiBoundsComposition* GuiControl::GetBoundsComposition()
			{
				EnsureControlTemplateExists();
				return boundsComposition;
			}

			compositions::GuiGraphicsComposition* GuiControl::GetContainerComposition()
			{
				EnsureControlTemplateExists();
				return containerComposition;
			}

			compositions::GuiGraphicsComposition* GuiControl::GetFocusableComposition()
			{
				EnsureControlTemplateExists();
				return focusableComposition;
			}

			GuiControl* GuiControl::GetParent()
			{
				return parent;
			}

			vint GuiControl::GetChildrenCount()
			{
				return children.Count();
			}

			GuiControl* GuiControl::GetChild(vint index)
			{
				return children[index];
			}

			bool GuiControl::AddChild(GuiControl* control)
			{
				return GetContainerComposition()->AddChild(control->GetBoundsComposition());
			}

			bool GuiControl::HasChild(GuiControl* control)
			{
				return children.Contains(control);
			}

			GuiControlHost* GuiControl::GetRelatedControlHost()
			{
				return parent?parent->GetRelatedControlHost():0;
			}

			bool GuiControl::GetVisuallyEnabled()
			{
				return isVisuallyEnabled;
			}

			bool GuiControl::GetEnabled()
			{
				return isEnabled;
			}

			void GuiControl::SetEnabled(bool value)
			{
				if(isEnabled!=value)
				{
					isEnabled=value;
					EnabledChanged.Execute(GetNotifyEventArguments());
					UpdateVisuallyEnabled();
				}
			}

			bool GuiControl::GetVisible()
			{
				return isVisible;
			}

			void GuiControl::SetVisible(bool value)
			{
				boundsComposition->SetVisible(value);
				if(isVisible!=value)
				{
					isVisible=value;
					VisibleChanged.Execute(GetNotifyEventArguments());
				}
			}

			const WString& GuiControl::GetAlt()
			{
				return alt;
			}

			bool GuiControl::SetAlt(const WString& value)
			{
				if (!IGuiAltAction::IsLegalAlt(value)) return false;
				if (alt != value)
				{
					alt = value;
					AltChanged.Execute(GetNotifyEventArguments());
				}
				return true;
			}

			void GuiControl::SetActivatingAltHost(compositions::IGuiAltActionHost* host)
			{
				activatingAltHost = host;
			}

			const WString& GuiControl::GetText()
			{
				return text;
			}

			void GuiControl::SetText(const WString& value)
			{
				if (text != value)
				{
					text = value;
					if (controlTemplateObject)
					{
						controlTemplateObject->SetText(text);
					}
					TextChanged.Execute(GetNotifyEventArguments());
				}
			}

			const FontProperties& GuiControl::GetFont()
			{
				return font;
			}

			void GuiControl::SetFont(const FontProperties& value)
			{
				if (font != value)
				{
					font = value;
					if (controlTemplateObject)
					{
						controlTemplateObject->SetFont(font);
					}
					FontChanged.Execute(GetNotifyEventArguments());
				}
			}
			
			description::Value GuiControl::GetContext()
			{
				return context;
			}

			void GuiControl::SetContext(const description::Value& value)
			{
				if (context != value)
				{
					context = value;
					if (controlTemplateObject)
					{
						controlTemplateObject->SetContext(context);
					}
					ContextChanged.Execute(GetNotifyEventArguments());
				}
			}

			void GuiControl::SetFocus()
			{
				if(focusableComposition)
				{
					GuiGraphicsHost* host=focusableComposition->GetRelatedGraphicsHost();
					if(host)
					{
						host->SetFocus(focusableComposition);
					}
				}
			}

			description::Value GuiControl::GetTag()
			{
				return tag;
			}

			void GuiControl::SetTag(const description::Value& value)
			{
				tag=value;
			}

			GuiControl* GuiControl::GetTooltipControl()
			{
				return tooltipControl;
			}

			GuiControl* GuiControl::SetTooltipControl(GuiControl* value)
			{
				GuiControl* oldTooltipControl=tooltipControl;
				tooltipControl=value;
				return oldTooltipControl;
			}

			vint GuiControl::GetTooltipWidth()
			{
				return tooltipWidth;
			}

			void GuiControl::SetTooltipWidth(vint value)
			{
				tooltipWidth=value;
			}

			bool GuiControl::DisplayTooltip(Point location)
			{
				if(!tooltipControl) return false;
				GetApplication()->ShowTooltip(this, tooltipControl, tooltipWidth, location);
				return true;
			}

			void GuiControl::CloseTooltip()
			{
				if(GetApplication()->GetTooltipOwner()==this)
				{
					GetApplication()->CloseTooltip();
				}
			}

			IDescriptable* GuiControl::QueryService(const WString& identifier)
			{
				if (identifier == IGuiAltAction::Identifier)
				{
					return (IGuiAltAction*)this;
				}
				else if (identifier == IGuiAltActionContainer::Identifier)
				{
					return 0;
				}
				else if(parent)
				{
					return parent->QueryService(identifier);
				}
				else
				{
					return 0;
				}
			}

/***********************************************************************
GuiCustomControl
***********************************************************************/

			controls::GuiControlHost* GuiCustomControl::GetControlHostForInstance()
			{
				return GetRelatedControlHost();
			}

			void GuiCustomControl::OnParentLineChanged()
			{
				GuiControl::OnParentLineChanged();
				OnControlHostForInstanceChanged();
			}

			GuiCustomControl::GuiCustomControl(theme::ThemeName themeName)
				:GuiControl(themeName)
			{
			}

			GuiCustomControl::~GuiCustomControl()
			{
				FinalizeAggregation();
				FinalizeInstanceRecursively(this);
			}
		}
	}
}

/***********************************************************************
.\CONTROLS\TEMPLATES\GUICONTROLSHARED.CPP
***********************************************************************/

namespace vl
{
	namespace presentation
	{
		namespace controls
		{
			using namespace reflection::description;
			using namespace compositions;

/***********************************************************************
GuiComponent
***********************************************************************/
			
			GuiComponent::GuiComponent()
			{
			}

			GuiComponent::~GuiComponent()
			{
			}

			void GuiComponent::Attach(GuiInstanceRootObject* rootObject)
			{
			}

			void GuiComponent::Detach(GuiInstanceRootObject* rootObject)
			{
			}

/***********************************************************************
GuiInstanceRootObject
***********************************************************************/

			class RootObjectTimerCallback : public Object, public IGuiGraphicsTimerCallback
			{
			public:
				GuiControlHost*					controlHost;
				GuiInstanceRootObject*			rootObject;
				bool							alive = true;

				RootObjectTimerCallback(GuiInstanceRootObject* _rootObject, GuiControlHost* _controlHost)
					:rootObject(_rootObject)
					, controlHost(_controlHost)
				{
				}

				bool Play()override
				{
					if (alive)
					{
						for (vint i = rootObject->runningAnimations.Count() - 1; i >= 0; i--)
						{
							auto animation = rootObject->runningAnimations[i];
							animation->Run();
							if (animation->GetStopped())
							{
								rootObject->runningAnimations.RemoveAt(i);
							}
						}

						if (rootObject->runningAnimations.Count() == 0)
						{
							rootObject->UninstallTimerCallback(nullptr);
							return false;
						}
					}
					return alive;
				}
			};
			
			void GuiInstanceRootObject::InstallTimerCallback(controls::GuiControlHost* controlHost)
			{
				if (!timerCallback)
				{
					timerCallback = new RootObjectTimerCallback(this, controlHost);
					controlHost->GetTimerManager()->AddCallback(timerCallback);
				}
			}

			bool GuiInstanceRootObject::UninstallTimerCallback(controls::GuiControlHost* controlHost)
			{
				if (timerCallback && timerCallback->controlHost != controlHost)
				{
					timerCallback->alive = false;
					timerCallback = nullptr;
					return true;
				}
				return false;
			}

			void GuiInstanceRootObject::OnControlHostForInstanceChanged()
			{
				auto controlHost = GetControlHostForInstance();
				if (UninstallTimerCallback(controlHost))
				{
					FOREACH(Ptr<IGuiAnimation>, animation, runningAnimations)
					{
						animation->Pause();
					}
				}

				if (controlHost)
				{
					InstallTimerCallback(controlHost);
					FOREACH(Ptr<IGuiAnimation>, animation, runningAnimations)
					{
						animation->Resume();
					}
					StartPendingAnimations();
				}
			}

			void GuiInstanceRootObject::StartPendingAnimations()
			{
				FOREACH(Ptr<IGuiAnimation>, animation, pendingAnimations)
				{
					animation->Start();
				}

				CopyFrom(runningAnimations, pendingAnimations, true);
				pendingAnimations.Clear();
			}

			GuiInstanceRootObject::GuiInstanceRootObject()
			{
			}

			GuiInstanceRootObject::~GuiInstanceRootObject()
			{
				UninstallTimerCallback(nullptr);
			}

			void GuiInstanceRootObject::FinalizeInstance()
			{
				if (!finalized)
				{
					finalized = true;

					FOREACH(Ptr<IValueSubscription>, subscription, subscriptions)
					{
						subscription->Close();
					}
					FOREACH(GuiComponent*, component, components)
					{
						component->Detach(this);
					}

					subscriptions.Clear();
					for (vint i = 0; i<components.Count(); i++)
					{
						delete components[i];
					}
					components.Clear();
				}
			}

			bool GuiInstanceRootObject::IsFinalized()
			{
				return finalized;
			}

			void GuiInstanceRootObject::FinalizeInstanceRecursively(templates::GuiTemplate* thisObject)
			{
				if (!finalized)
				{
					NotifyFinalizeInstance(thisObject);
				}
			}

			void GuiInstanceRootObject::FinalizeInstanceRecursively(GuiCustomControl* thisObject)
			{
				if (!finalized)
				{
					NotifyFinalizeInstance(thisObject);
				}
			}

			void GuiInstanceRootObject::FinalizeInstanceRecursively(GuiControlHost* thisObject)
			{
				if (!finalized)
				{
					NotifyFinalizeInstance(thisObject);
				}
			}

			void GuiInstanceRootObject::FinalizeGeneralInstance(GuiInstanceRootObject* thisObject)
			{
			}

			void GuiInstanceRootObject::SetResourceResolver(Ptr<GuiResourcePathResolver> resolver)
			{
				resourceResolver = resolver;
			}

			Ptr<DescriptableObject> GuiInstanceRootObject::ResolveResource(const WString& protocol, const WString& path, bool ensureExist)
			{
				Ptr<DescriptableObject> object;
				if (resourceResolver)
				{
					object = resourceResolver->ResolveResource(protocol, path);
				}
				if (ensureExist && !object)
				{
					throw ArgumentException(L"Resource \"" + protocol + L"://" + path + L"\" does not exist.");
				}
				return object;
			}

			Ptr<description::IValueSubscription> GuiInstanceRootObject::AddSubscription(Ptr<description::IValueSubscription> subscription)
			{
				CHECK_ERROR(finalized == false, L"GuiInstanceRootObject::AddSubscription(Ptr<IValueSubscription>)#Cannot add subscription after finalizing.");
				if (subscriptions.Contains(subscription.Obj()))
				{
					return nullptr;
				}
				else
				{
					subscriptions.Add(subscription);
					subscription->Open();
					subscription->Update();
					return subscription;
				}
			}

			void GuiInstanceRootObject::UpdateSubscriptions()
			{
				FOREACH(Ptr<IValueSubscription>, subscription, subscriptions)
				{
					subscription->Update();
				}
			}

			bool GuiInstanceRootObject::AddComponent(GuiComponent* component)
			{
				CHECK_ERROR(finalized == false, L"GuiInstanceRootObject::AddComponent(GuiComponent*)#Cannot add component after finalizing.");
				if(components.Contains(component))
				{
					return false;
				}
				else
				{
					components.Add(component);
					component->Attach(this);
					return true;
				}
			}

			bool GuiInstanceRootObject::AddControlHostComponent(GuiControlHost* controlHost)
			{
				return AddComponent(new GuiObjectComponent<GuiControlHost>(controlHost));
			}

			bool GuiInstanceRootObject::AddAnimation(Ptr<IGuiAnimation> animation)
			{
				CHECK_ERROR(finalized == false, L"GuiInstanceRootObject::AddAnimation(Ptr<IGuiAnimation>)#Cannot add animation after finalizing.");
				if (runningAnimations.Contains(animation.Obj()) || pendingAnimations.Contains(animation.Obj()))
				{
					return false;
				}
				else
				{
					pendingAnimations.Add(animation);

					if (auto controlHost = GetControlHostForInstance())
					{
						InstallTimerCallback(controlHost);
						StartPendingAnimations();
					}
					return true;
				}
			}

			bool GuiInstanceRootObject::KillAnimation(Ptr<IGuiAnimation> animation)
			{
				if (!animation) return false;
				if (runningAnimations.Contains(animation.Obj()))
				{
					runningAnimations.Remove(animation.Obj());
					return true;
				}
				if (pendingAnimations.Contains(animation.Obj()))
				{
					pendingAnimations.Remove(animation.Obj());
					return true;
				}
				return false;
			}
		}
	}
}

/***********************************************************************
.\CONTROLS\TEMPLATES\GUITHEMESTYLEFACTORY.CPP
***********************************************************************/

namespace vl
{
	namespace presentation
	{
		namespace theme
		{
			using namespace collections;
			using namespace controls;
			using namespace templates;

			class Theme : public Object, public virtual theme::ITheme
			{
			public:
				Dictionary<WString, Ptr<ThemeTemplates>>	templates;
				ThemeTemplates*								first = nullptr;
				ThemeTemplates*								last = nullptr;

				bool RegisterTheme(const WString& name, Ptr<ThemeTemplates> theme)
				{
					CHECK_ERROR(theme->previous == nullptr, L"vl::presentation::theme::RegisterTheme(const WString&, Ptr<ThemeTemplates>)#Theme object has been registered");
					CHECK_ERROR(theme->next == nullptr, L"vl::presentation::theme::RegisterTheme(const WString&, Ptr<ThemeTemplates>)#Theme object has been registered");

					if (templates.Keys().Contains(name))
					{
						return false;
					}
					templates.Add(name, theme);

					if (last)
					{
						last->next = theme.Obj();
					}
					theme->previous = last;
					last = theme.Obj();

					return true;
				}

				Ptr<ThemeTemplates> UnregisterTheme(const WString& name)
				{
					vint index = templates.Keys().IndexOf(name);
					if (index == -1)
					{
						return nullptr;
					}

					auto themeTemplates = templates.Values().Get(index);

					if (themeTemplates->previous)
					{
						themeTemplates->previous->next = themeTemplates->next;
					}
					else
					{
						first = themeTemplates->next;
					}

					if (themeTemplates->next)
					{
						themeTemplates->next->previous = themeTemplates->previous;

					}
					else
					{
						last = themeTemplates->previous;
					}

					templates.Remove(name);
					return themeTemplates;
				}

				TemplateProperty<templates::GuiControlTemplate> CreateStyle(ThemeName themeName)override
				{
					switch (themeName)
					{
#define GUI_DEFINE_ITEM_PROPERTY(TEMPLATE, CONTROL) \
					case ThemeName::CONTROL:\
						{\
							auto current = last;\
							while (current) \
							{\
								if (current->CONTROL)\
								{\
									return current->CONTROL; \
								}\
								current = current->previous;\
							}\
							throw Exception(L"Control template for \"" L ## #CONTROL L"\" is not defined.");\
						}\

						GUI_CONTROL_TEMPLATE_TYPES(GUI_DEFINE_ITEM_PROPERTY)
#undef GUI_DEFINE_ITEM_PROPERTY
					default:
						CHECK_FAIL(L"vl::presentation::theme::ITheme::CreateStyle(ThemeName)#Unknown theme name.");
					}
				}
			};

			controls::GuiControlHost* ThemeTemplates::GetControlHostForInstance()
			{
				return nullptr;
			}

			ThemeTemplates::~ThemeTemplates()
			{
				FinalizeAggregation();
			}

			Theme* currentTheme = nullptr;

			ITheme* GetCurrentTheme()
			{
				return currentTheme;
			}

			void InitializeTheme()
			{
				CHECK_ERROR(currentTheme == nullptr, L"vl::presentation::theme::InitializeTheme()#Theme has already been initialized");
				currentTheme = new Theme;
			}

			void FinalizeTheme()
			{
				CHECK_ERROR(currentTheme != nullptr, L"vl::presentation::theme::FinalizeTheme()#Theme has not been initialized");
				delete currentTheme;
				currentTheme = nullptr;
			}

			bool RegisterTheme(const WString& name, Ptr<ThemeTemplates> theme)
			{
				CHECK_ERROR(currentTheme != nullptr, L"vl::presentation::theme::RegisterTheme(const WString&, Ptr<ThemeTemplates>)#Theme has already been initialized");
				return currentTheme->RegisterTheme(name, theme);
			}

			Ptr<ThemeTemplates> UnregisterTheme(const WString& name)
			{
				CHECK_ERROR(currentTheme != nullptr, L"vl::presentation::theme::UnregisterTheme(const WString&)#Theme has already been initialized");
				return currentTheme->UnregisterTheme(name);
			}
		}
	}
}

/***********************************************************************
.\GRAPHICSELEMENT\GUIGRAPHICSHOST.CPP
***********************************************************************/

namespace vl
{
	namespace presentation
	{
		namespace compositions
		{
			using namespace collections;
			using namespace controls;
			using namespace elements;
			using namespace theme;

/***********************************************************************
GuiGraphicsTimerManager
***********************************************************************/

			GuiGraphicsTimerManager::GuiGraphicsTimerManager()
			{
			}

			GuiGraphicsTimerManager::~GuiGraphicsTimerManager()
			{
			}

			void GuiGraphicsTimerManager::AddCallback(Ptr<IGuiGraphicsTimerCallback> callback)
			{
				callbacks.Add(callback);
			}

			void GuiGraphicsTimerManager::Play()
			{
				for (vint i = callbacks.Count() - 1; i >= 0; i--)
				{
					auto callback = callbacks[i];
					if (!callback->Play())
					{
						callbacks.RemoveAt(i);
					}
				}
			}

/***********************************************************************
IGuiAltAction
***********************************************************************/

			const wchar_t* const IGuiAltAction::Identifier = L"vl::presentation::compositions::IGuiAltAction";
			const wchar_t* const IGuiAltActionContainer::Identifier = L"vl::presentation::compositions::IGuiAltAction";
			const wchar_t* const IGuiAltActionHost::Identifier = L"vl::presentation::compositions::IGuiAltAction";

			bool IGuiAltAction::IsLegalAlt(const WString& alt)
			{
				for (vint i = 0; i < alt.Length(); i++)
				{
					if (alt[i] < L'A' || L'Z' < alt[i])
					{
						return false;
					}
				}
				return true;
			}

			void IGuiAltActionHost::CollectAltActionsFromControl(controls::GuiControl* control, collections::Group<WString, IGuiAltAction*>& actions)
			{
				List<GuiControl*> controls;
				controls.Add(control);
				vint current = 0;

				while (current < controls.Count())
				{
					GuiControl* control = controls[current++];

					if (auto container = control->QueryTypedService<IGuiAltActionContainer>())
					{
						vint count = container->GetAltActionCount();
						for (vint i = 0; i < count; i++)
						{
							auto action = container->GetAltAction(i);
							actions.Add(action->GetAlt(), action);
						}
						continue;
					}
					else if (auto action = control->QueryTypedService<IGuiAltAction>())
					{
						if (action->IsAltAvailable())
						{
							if (action->IsAltEnabled())
							{
								actions.Add(action->GetAlt(), action);
								continue;
							}
						}
					}

					vint count = control->GetChildrenCount();
					for (vint i = 0; i < count; i++)
					{
						controls.Add(control->GetChild(i));
					}
				}
			}

/***********************************************************************
GuiGraphicsHost
***********************************************************************/

			void GuiGraphicsHost::EnterAltHost(IGuiAltActionHost* host)
			{
				ClearAltHost();

				Group<WString, IGuiAltAction*> actions;
				host->CollectAltActions(actions);
				if (actions.Count() == 0)
				{
					CloseAltHost();
					return;
				}

				host->OnActivatedAltHost(currentAltHost);
				currentAltHost = host;
				CreateAltTitles(actions);
			}

			void GuiGraphicsHost::LeaveAltHost()
			{
				if (currentAltHost)
				{
					ClearAltHost();
					auto previousHost = currentAltHost->GetPreviousAltHost();
					currentAltHost->OnDeactivatedAltHost();
					currentAltHost = previousHost;

					if (currentAltHost)
					{
						Group<WString, IGuiAltAction*> actions;
						currentAltHost->CollectAltActions(actions);
						CreateAltTitles(actions);
					}
				}
			}

			bool GuiGraphicsHost::EnterAltKey(wchar_t key)
			{
				currentAltPrefix += key;
				vint index = currentActiveAltActions.Keys().IndexOf(currentAltPrefix);
				if (index == -1)
				{
					if (FilterTitles() == 0)
					{
						currentAltPrefix = currentAltPrefix.Left(currentAltPrefix.Length() - 1);
						FilterTitles();
					}
				}
				else
				{
					auto action = currentActiveAltActions.Values()[index];
					if (action->GetActivatingAltHost())
					{
						EnterAltHost(action->GetActivatingAltHost());
					}
					else
					{
						CloseAltHost();
					}
					action->OnActiveAlt();
					return true;
				}
				return false;
			}

			void GuiGraphicsHost::LeaveAltKey()
			{
				if (currentAltPrefix.Length() >= 1)
				{
					currentAltPrefix = currentAltPrefix.Left(currentAltPrefix.Length() - 1);
				}
				FilterTitles();
			}

			void GuiGraphicsHost::CreateAltTitles(const collections::Group<WString, IGuiAltAction*>& actions)
			{
				if (currentAltHost)
				{
					vint count = actions.Count();
					for (vint i = 0; i < count; i++)
					{
						WString key = actions.Keys()[i];
						const auto& values = actions.GetByIndex(i);
						vint numberLength = 0;
						if (values.Count() == 1 && key.Length() > 0)
						{
							numberLength = 0;
						}
						else if (values.Count() <= 10)
						{
							numberLength = 1;
						}
						else if (values.Count() <= 100)
						{
							numberLength = 2;
						}
						else if (values.Count() <= 1000)
						{
							numberLength = 3;
						}
						else
						{
							continue;
						}

						FOREACH_INDEXER(IGuiAltAction*, action, index, values)
						{
							WString key = actions.Keys()[i];
							if (numberLength > 0)
							{
								WString number = itow(index);
								while (number.Length() < numberLength)
								{
									number = L"0" + number;
								}
								key += number;
							}
							currentActiveAltActions.Add(key, action);
						}
					}

					count = currentActiveAltActions.Count();
					auto window = dynamic_cast<GuiWindow*>(currentAltHost->GetAltComposition()->GetRelatedControlHost());
					for (vint i = 0; i < count; i++)
					{
						auto key = currentActiveAltActions.Keys()[i];
						auto composition = currentActiveAltActions.Values()[i]->GetAltComposition();

						auto label = new GuiLabel(theme::ThemeName::ShortcutKey);
						if (auto labelStyle = window->GetControlTemplateObject(true)->GetShortcutKeyTemplate())
						{
							label->SetControlTemplate(labelStyle);
						}
						label->SetText(key);
						composition->AddChild(label->GetBoundsComposition());
						currentActiveAltTitles.Add(key, label);
					}

					FilterTitles();
				}
			}

			vint GuiGraphicsHost::FilterTitles()
			{
				vint count = currentActiveAltTitles.Count();
				vint visibles = 0;
				for (vint i = 0; i < count; i++)
				{
					auto key = currentActiveAltTitles.Keys()[i];
					auto value = currentActiveAltTitles.Values()[i];
					if (key.Length() >= currentAltPrefix.Length() && key.Left(currentAltPrefix.Length()) == currentAltPrefix)
					{
						value->SetVisible(true);
						if (currentAltPrefix.Length() <= key.Length())
						{
							value->SetText(
								key
								.Insert(currentAltPrefix.Length(), L"[")
								.Insert(currentAltPrefix.Length() + 2, L"]")
								);
						}
						else
						{
							value->SetText(key);
						}
						visibles++;
					}
					else
					{
						value->SetVisible(false);
					}
				}
				return visibles;
			}

			void GuiGraphicsHost::ClearAltHost()
			{
				FOREACH(GuiControl*, title, currentActiveAltTitles.Values())
				{
					SafeDeleteControl(title);
				}
				currentActiveAltActions.Clear();
				currentActiveAltTitles.Clear();
				currentAltPrefix = L"";
			}

			void GuiGraphicsHost::CloseAltHost()
			{
				ClearAltHost();
				while (currentAltHost)
				{
					currentAltHost->OnDeactivatedAltHost();
					currentAltHost = currentAltHost->GetPreviousAltHost();
				}
			}

			void GuiGraphicsHost::RefreshRelatedHostRecord(INativeWindow* nativeWindow)
			{
				hostRecord.nativeWindow = nativeWindow;
				hostRecord.renderTarget = nativeWindow ? GetGuiGraphicsResourceManager()->GetRenderTarget(nativeWindow) : nullptr;
				windowComposition->UpdateRelatedHostRecord(&hostRecord);
			}

			void GuiGraphicsHost::DisconnectCompositionInternal(GuiGraphicsComposition* composition)
			{
				for(vint i=0;i<composition->Children().Count();i++)
				{
					DisconnectCompositionInternal(composition->Children().Get(i));
				}
				if(mouseCaptureComposition==composition)
				{
					if(hostRecord.nativeWindow)
					{
						hostRecord.nativeWindow->ReleaseCapture();
					}
					mouseCaptureComposition=0;
				}
				if(focusedComposition==composition)
				{
					focusedComposition=0;
				}
				mouseEnterCompositions.Remove(composition);
			}

			void GuiGraphicsHost::MouseCapture(const NativeWindowMouseInfo& info)
			{
				if(hostRecord.nativeWindow && (info.left || info.middle || info.right))
				{
					if(!hostRecord.nativeWindow->IsCapturing() && !info.nonClient)
					{
						hostRecord.nativeWindow->RequireCapture();
						mouseCaptureComposition=windowComposition->FindComposition(Point(info.x, info.y), true);
					}
				}
			}

			void GuiGraphicsHost::MouseUncapture(const NativeWindowMouseInfo& info)
			{
				if(hostRecord.nativeWindow && !(info.left || info.middle || info.right))
				{
					hostRecord.nativeWindow->ReleaseCapture();
					mouseCaptureComposition=0;
				}
			}

			void GuiGraphicsHost::OnCharInput(const NativeWindowCharInfo& info, GuiGraphicsComposition* composition, GuiCharEvent GuiGraphicsEventReceiver::* eventReceiverEvent)
			{
				List<GuiGraphicsComposition*> compositions;
				while(composition)
				{
					if(composition->HasEventReceiver())
					{
						compositions.Add(composition);
					}
					composition=composition->GetParent();
				}

				GuiCharEventArgs arguments(composition);
				(NativeWindowCharInfo&)arguments=info;

				for(vint i=compositions.Count()-1;i>=0;i--)
				{
					compositions[i]->GetEventReceiver()->previewCharInput.Execute(arguments);
					if(arguments.handled)
					{
						return;
					}
				}

				for(vint i=0;i<compositions.Count();i++)
				{
					(compositions[i]->GetEventReceiver()->*eventReceiverEvent).Execute(arguments);
					if(arguments.handled)
					{
						return;
					}
				}
			}

			void GuiGraphicsHost::OnKeyInput(const NativeWindowKeyInfo& info, GuiGraphicsComposition* composition, GuiKeyEvent GuiGraphicsEventReceiver::* eventReceiverEvent)
			{
				List<GuiGraphicsComposition*> compositions;
				while(composition)
				{
					if(composition->HasEventReceiver())
					{
						compositions.Add(composition);
					}
					composition=composition->GetParent();
				}

				GuiKeyEventArgs arguments(composition);
				(NativeWindowKeyInfo&)arguments=info;

				for(vint i=compositions.Count()-1;i>=0;i--)
				{
					compositions[i]->GetEventReceiver()->previewKey.Execute(arguments);
					if(arguments.handled)
					{
						return;
					}
				}

				for(vint i=0;i<compositions.Count();i++)
				{
					(compositions[i]->GetEventReceiver()->*eventReceiverEvent).Execute(arguments);
					if(arguments.handled)
					{
						return;
					}
				}
			}

			void GuiGraphicsHost::RaiseMouseEvent(GuiMouseEventArgs& arguments, GuiGraphicsComposition* composition, GuiMouseEvent GuiGraphicsEventReceiver::* eventReceiverEvent)
			{
				arguments.compositionSource=composition;
				arguments.eventSource=0;
				vint x=arguments.x;
				vint y=arguments.y;

				while(composition)
				{
					if(composition->HasEventReceiver())
					{
						if(!arguments.eventSource)
						{
							arguments.eventSource=composition;
						}
						GuiGraphicsEventReceiver* eventReceiver=composition->GetEventReceiver();
						(eventReceiver->*eventReceiverEvent).Execute(arguments);
						if(arguments.handled)
						{
							break;
						}
					}

					GuiGraphicsComposition* parent=composition->GetParent();
					if(parent)
					{
						Rect parentBounds=parent->GetBounds();
						Rect clientArea=parent->GetClientArea();
						Rect childBounds=composition->GetBounds();

						x+=childBounds.x1+(clientArea.x1-parentBounds.x1);
						y+=childBounds.y1+(clientArea.y1-parentBounds.y1);
						arguments.x=x;
						arguments.y=y;
					}
					composition=parent;
				}
			}

			void GuiGraphicsHost::OnMouseInput(const NativeWindowMouseInfo& info, GuiMouseEvent GuiGraphicsEventReceiver::* eventReceiverEvent)
			{
				GuiGraphicsComposition* composition=0;
				if(mouseCaptureComposition)
				{
					composition=mouseCaptureComposition;
				}
				else
				{
					composition=windowComposition->FindComposition(Point(info.x, info.y), true);
				}
				if(composition)
				{
					Rect bounds=composition->GetGlobalBounds();
					GuiMouseEventArgs arguments;
					(NativeWindowMouseInfo&)arguments=info;
					arguments.x-=bounds.x1;
					arguments.y-=bounds.y1;
					RaiseMouseEvent(arguments, composition, eventReceiverEvent);
				}
			}

			INativeWindowListener::HitTestResult GuiGraphicsHost::HitTest(Point location)
			{
				Rect bounds = hostRecord.nativeWindow->GetBounds();
				Rect clientBounds = hostRecord.nativeWindow->GetClientBoundsInScreen();
				Point clientLocation(location.x + bounds.x1 - clientBounds.x1, location.y + bounds.y1 - clientBounds.y1);
				GuiGraphicsComposition* hitComposition = windowComposition->FindComposition(clientLocation, false);
				while (hitComposition)
				{
					INativeWindowListener::HitTestResult result = hitComposition->GetAssociatedHitTestResult();
					if (result == INativeWindowListener::NoDecision)
					{
						hitComposition = hitComposition->GetParent();
					}
					else
					{
						return result;
					}
				}
				return INativeWindowListener::NoDecision;
			}

			void GuiGraphicsHost::Moving(Rect& bounds, bool fixSizeOnly)
			{
				Rect oldBounds = hostRecord.nativeWindow->GetBounds();
				minSize = windowComposition->GetPreferredBounds().GetSize();
				Size minWindowSize = minSize + (oldBounds.GetSize() - hostRecord.nativeWindow->GetClientSize());
				if (bounds.Width() < minWindowSize.x)
				{
					if (fixSizeOnly)
					{
						if (bounds.Width() < minWindowSize.x)
						{
							bounds.x2 = bounds.x1 + minWindowSize.x;
						}
					}
					else if (oldBounds.x1 != bounds.x1)
					{
						bounds.x1 = oldBounds.x2 - minWindowSize.x;
					}
					else if (oldBounds.x2 != bounds.x2)
					{
						bounds.x2 = oldBounds.x1 + minWindowSize.x;
					}
				}
				if (bounds.Height() < minWindowSize.y)
				{
					if (fixSizeOnly)
					{
						if (bounds.Height() < minWindowSize.y)
						{
							bounds.y2 = bounds.y1 + minWindowSize.y;
						}
					}
					else if (oldBounds.y1 != bounds.y1)
					{
						bounds.y1 = oldBounds.y2 - minWindowSize.y;
					}
					else if (oldBounds.y2 != bounds.y2)
					{
						bounds.y2 = oldBounds.y1 + minWindowSize.y;
					}
				}
			}

			void GuiGraphicsHost::Moved()
			{
				Size size = hostRecord.nativeWindow->GetClientSize();
				if (previousClientSize != size)
				{
					previousClientSize = size;
					minSize = windowComposition->GetPreferredBounds().GetSize();
					needRender = true;
				}
			}

			void GuiGraphicsHost::Paint()
			{
				if (!supressPaint)
				{
					needRender = true;
				}
			}

			void GuiGraphicsHost::LeftButtonDown(const NativeWindowMouseInfo& info)
			{
				CloseAltHost();
				MouseCapture(info);
				OnMouseInput(info, &GuiGraphicsEventReceiver::leftButtonDown);
			}

			void GuiGraphicsHost::LeftButtonUp(const NativeWindowMouseInfo& info)
			{
				OnMouseInput(info, &GuiGraphicsEventReceiver::leftButtonUp);
				MouseUncapture(info);
			}

			void GuiGraphicsHost::LeftButtonDoubleClick(const NativeWindowMouseInfo& info)
			{
				LeftButtonDown(info);
				OnMouseInput(info, &GuiGraphicsEventReceiver::leftButtonDoubleClick);
			}

			void GuiGraphicsHost::RightButtonDown(const NativeWindowMouseInfo& info)
			{
				CloseAltHost();
				MouseCapture(info);
				OnMouseInput(info, &GuiGraphicsEventReceiver::rightButtonDown);
			}

			void GuiGraphicsHost::RightButtonUp(const NativeWindowMouseInfo& info)
			{
				OnMouseInput(info, &GuiGraphicsEventReceiver::rightButtonUp);
				MouseUncapture(info);
			}

			void GuiGraphicsHost::RightButtonDoubleClick(const NativeWindowMouseInfo& info)
			{
				RightButtonDown(info);
				OnMouseInput(info, &GuiGraphicsEventReceiver::rightButtonDoubleClick);
			}

			void GuiGraphicsHost::MiddleButtonDown(const NativeWindowMouseInfo& info)
			{
				CloseAltHost();
				MouseCapture(info);
				OnMouseInput(info, &GuiGraphicsEventReceiver::middleButtonDown);
			}

			void GuiGraphicsHost::MiddleButtonUp(const NativeWindowMouseInfo& info)
			{
				OnMouseInput(info, &GuiGraphicsEventReceiver::middleButtonUp);
				MouseUncapture(info);
			}

			void GuiGraphicsHost::MiddleButtonDoubleClick(const NativeWindowMouseInfo& info)
			{
				MiddleButtonDown(info);
				OnMouseInput(info, &GuiGraphicsEventReceiver::middleButtonDoubleClick);
			}

			void GuiGraphicsHost::HorizontalWheel(const NativeWindowMouseInfo& info)
			{
				OnMouseInput(info, &GuiGraphicsEventReceiver::horizontalWheel);
			}

			void GuiGraphicsHost::VerticalWheel(const NativeWindowMouseInfo& info)
			{
				OnMouseInput(info, &GuiGraphicsEventReceiver::verticalWheel);
			}

			void GuiGraphicsHost::MouseMoving(const NativeWindowMouseInfo& info)
			{
				CompositionList newCompositions;
				{
					GuiGraphicsComposition* composition = windowComposition->FindComposition(Point(info.x, info.y), true);
					while (composition)
					{
						newCompositions.Insert(0, composition);
						composition = composition->GetParent();
					}
				}

				vint firstDifferentIndex = mouseEnterCompositions.Count();
				for (vint i = 0; i < mouseEnterCompositions.Count(); i++)
				{
					if (i == newCompositions.Count())
					{
						firstDifferentIndex = newCompositions.Count();
						break;
					}
					if (mouseEnterCompositions[i] != newCompositions[i])
					{
						firstDifferentIndex = i;
						break;
					}
				}

				for (vint i = mouseEnterCompositions.Count() - 1; i >= firstDifferentIndex; i--)
				{
					GuiGraphicsComposition* composition = mouseEnterCompositions[i];
					if (composition->HasEventReceiver())
					{
						composition->GetEventReceiver()->mouseLeave.Execute(GuiEventArgs(composition));
					}
				}

				CopyFrom(mouseEnterCompositions, newCompositions);
				for (vint i = firstDifferentIndex; i < mouseEnterCompositions.Count(); i++)
				{
					GuiGraphicsComposition* composition = mouseEnterCompositions[i];
					if (composition->HasEventReceiver())
					{
						composition->GetEventReceiver()->mouseEnter.Execute(GuiEventArgs(composition));
					}
				}

				INativeCursor* cursor = 0;
				if (newCompositions.Count() > 0)
				{
					cursor = newCompositions[newCompositions.Count() - 1]->GetRelatedCursor();
				}
				if (cursor)
				{
					hostRecord.nativeWindow->SetWindowCursor(cursor);
				}
				else
				{
					hostRecord.nativeWindow->SetWindowCursor(GetCurrentController()->ResourceService()->GetDefaultSystemCursor());
				}

				OnMouseInput(info, &GuiGraphicsEventReceiver::mouseMove);
			}

			void GuiGraphicsHost::MouseEntered()
			{
			}

			void GuiGraphicsHost::MouseLeaved()
			{
				for(vint i=mouseEnterCompositions.Count()-1;i>=0;i--)
				{
					GuiGraphicsComposition* composition=mouseEnterCompositions[i];
					if(composition->HasEventReceiver())
					{
						composition->GetEventReceiver()->mouseLeave.Execute(GuiEventArgs(composition));
					}
				}
				mouseEnterCompositions.Clear();
			}

			void GuiGraphicsHost::KeyDown(const NativeWindowKeyInfo& info)
			{
				if (!info.ctrl && !info.shift && currentAltHost)
				{
					if (info.code == VKEY_ESCAPE)
					{
						LeaveAltHost();
						return;
					}
					else if (info.code == VKEY_BACK)
					{
						LeaveAltKey();
					}
					else if (VKEY_NUMPAD0 <= info.code && info.code <= VKEY_NUMPAD9)
					{
						if (EnterAltKey((wchar_t)(L'0' + (info.code - VKEY_NUMPAD0))))
						{
							supressAltKey = info.code;
							return;
						}
					}
					else if (('0' <= info.code && info.code <= '9') || ('A' <= info.code && info.code <= 'Z'))
					{
						if (EnterAltKey((wchar_t)info.code))
						{
							supressAltKey = info.code;
							return;
						}
					}
				}

				if (currentAltHost)
				{
					return;
				}
				
				if(shortcutKeyManager && shortcutKeyManager->Execute(info))
				{
					return;
				}
				if(focusedComposition && focusedComposition->HasEventReceiver())
				{
					OnKeyInput(info, focusedComposition, &GuiGraphicsEventReceiver::keyDown);
				}
			}

			void GuiGraphicsHost::KeyUp(const NativeWindowKeyInfo& info)
			{
				if (!info.ctrl && !info.shift && info.code == supressAltKey)
				{
					supressAltKey = 0;
					return;
				}

				if(focusedComposition && focusedComposition->HasEventReceiver())
				{
					OnKeyInput(info, focusedComposition, &GuiGraphicsEventReceiver::keyUp);
				}
			}

			void GuiGraphicsHost::SysKeyDown(const NativeWindowKeyInfo& info)
			{
				if (!info.ctrl && !info.shift && info.code == VKEY_MENU && !currentAltHost)
				{
					if (auto window = dynamic_cast<GuiWindow*>(windowComposition->Children()[0]->GetRelatedControlHost()))
					{
						if (auto altHost = window->QueryTypedService<IGuiAltActionHost>())
						{
							if (!altHost->GetPreviousAltHost())
							{
								EnterAltHost(altHost);
							}
						}
					}
				}

				if (currentAltHost)
				{
					return;
				}

				if(focusedComposition && focusedComposition->HasEventReceiver())
				{
					OnKeyInput(info, focusedComposition, &GuiGraphicsEventReceiver::systemKeyDown);
				}
			}

			void GuiGraphicsHost::SysKeyUp(const NativeWindowKeyInfo& info)
			{
				if (!info.ctrl && !info.shift && info.code == VKEY_MENU && hostRecord.nativeWindow)
				{
					if (hostRecord.nativeWindow)
					{
						hostRecord.nativeWindow->SupressAlt();
					}
				}

				if (focusedComposition && focusedComposition->HasEventReceiver())
				{
					OnKeyInput(info, focusedComposition, &GuiGraphicsEventReceiver::systemKeyUp);
				}
			}

			void GuiGraphicsHost::Char(const NativeWindowCharInfo& info)
			{
				if (!currentAltHost && !supressAltKey)
				{
					if(focusedComposition && focusedComposition->HasEventReceiver())
					{
						OnCharInput(info, focusedComposition, &GuiGraphicsEventReceiver::charInput);
					}
				}
			}

			void GuiGraphicsHost::GlobalTimer()
			{
				timerManager.Play();

				DateTime now=DateTime::UtcTime();
				if(now.totalMilliseconds-lastCaretTime>=CaretInterval)
				{
					lastCaretTime=now.totalMilliseconds;
					if(focusedComposition && focusedComposition->HasEventReceiver())
					{
						focusedComposition->GetEventReceiver()->caretNotify.Execute(GuiEventArgs(focusedComposition));
					}
				}
				
				Render(false);
			}

			GuiGraphicsHost::GuiGraphicsHost(controls::GuiControlHost* _controlHost, GuiGraphicsComposition* boundsComposition)
				:controlHost(_controlHost)
			{
				hostRecord.host = this;
				windowComposition=new GuiWindowComposition;
				windowComposition->SetMinSizeLimitation(GuiGraphicsComposition::LimitToElementAndChildren);
				windowComposition->AddChild(boundsComposition);
				RefreshRelatedHostRecord(nullptr);
			}

			GuiGraphicsHost::~GuiGraphicsHost()
			{
				windowComposition->RemoveChild(windowComposition->Children()[0]);
				NotifyFinalizeInstance(windowComposition);
				if(shortcutKeyManager)
				{
					delete shortcutKeyManager;
					shortcutKeyManager=0;
				}
				delete windowComposition;
			}

			INativeWindow* GuiGraphicsHost::GetNativeWindow()
			{
				return hostRecord.nativeWindow;
			}

			void GuiGraphicsHost::SetNativeWindow(INativeWindow* _nativeWindow)
			{
				if (hostRecord.nativeWindow != _nativeWindow)
				{
					if (hostRecord.nativeWindow)
					{
						GetCurrentController()->CallbackService()->UninstallListener(this);
						hostRecord.nativeWindow->UninstallListener(this);
					}

					if (_nativeWindow)
					{
						_nativeWindow->InstallListener(this);
						GetCurrentController()->CallbackService()->InstallListener(this);
						previousClientSize = _nativeWindow->GetClientSize();
						minSize = windowComposition->GetPreferredBounds().GetSize();
						_nativeWindow->SetCaretPoint(caretPoint);
						needRender = true;
					}

					RefreshRelatedHostRecord(_nativeWindow);
				}
			}

			GuiGraphicsComposition* GuiGraphicsHost::GetMainComposition()
			{
				return windowComposition;
			}

			void GuiGraphicsHost::Render(bool forceUpdate)
			{
				if (!forceUpdate && !needRender)
				{
					return;
				}
				needRender = false;

				if(hostRecord.nativeWindow && hostRecord.nativeWindow->IsVisible())
				{
					supressPaint = true;
					hostRecord.renderTarget->StartRendering();
					windowComposition->Render(Size());
					{
						auto bounds = windowComposition->GetBounds();
						auto preferred = windowComposition->GetPreferredBounds();
						auto width = bounds.Width() > preferred.Width() ? bounds.Width() : preferred.Width();
						auto height = bounds.Height() > preferred.Height() ? bounds.Height() : preferred.Height();
						if (width != bounds.Width() || height != bounds.Height())
						{
							controlHost->UpdateClientSizeAfterRendering(Size(width, height));
						}
					}
					auto result = hostRecord.renderTarget->StopRendering();
					hostRecord.nativeWindow->RedrawContent();
					supressPaint = false;

					switch (result)
					{
					case RenderTargetFailure::ResizeWhileRendering:
						{
							GetGuiGraphicsResourceManager()->ResizeRenderTarget(hostRecord.nativeWindow);
							needRender = true;
						}
						break;
					case RenderTargetFailure::LostDevice:
						{
							windowComposition->UpdateRelatedHostRecord(nullptr);
							GetGuiGraphicsResourceManager()->RecreateRenderTarget(hostRecord.nativeWindow);
							RefreshRelatedHostRecord(hostRecord.nativeWindow);
							needRender = true;
						}
						break;
					default:;
					}
				}
			}

			void GuiGraphicsHost::RequestRender()
			{
				needRender = true;
			}

			IGuiShortcutKeyManager* GuiGraphicsHost::GetShortcutKeyManager()
			{
				return shortcutKeyManager;
			}

			void GuiGraphicsHost::SetShortcutKeyManager(IGuiShortcutKeyManager* value)
			{
				shortcutKeyManager=value;
			}

			bool GuiGraphicsHost::SetFocus(GuiGraphicsComposition* composition)
			{
				if(!composition || composition->GetRelatedGraphicsHost()!=this)
				{
					return false;
				}
				if(focusedComposition && focusedComposition->HasEventReceiver())
				{
					GuiEventArgs arguments;
					arguments.compositionSource=focusedComposition;
					arguments.eventSource=focusedComposition;
					focusedComposition->GetEventReceiver()->lostFocus.Execute(arguments);
				}
				focusedComposition=composition;
				SetCaretPoint(Point(0, 0));
				if(focusedComposition && focusedComposition->HasEventReceiver())
				{
					GuiEventArgs arguments;
					arguments.compositionSource=focusedComposition;
					arguments.eventSource=focusedComposition;
					focusedComposition->GetEventReceiver()->gotFocus.Execute(arguments);
				}
				return true;
			}

			GuiGraphicsComposition* GuiGraphicsHost::GetFocusedComposition()
			{
				return focusedComposition;
			}

			Point GuiGraphicsHost::GetCaretPoint()
			{
				return caretPoint;
			}

			void GuiGraphicsHost::SetCaretPoint(Point value, GuiGraphicsComposition* referenceComposition)
			{
				if (referenceComposition)
				{
					Rect bounds = referenceComposition->GetGlobalBounds();
					value.x += bounds.x1;
					value.y += bounds.y1;
				}
				caretPoint = value;
				if (hostRecord.nativeWindow)
				{
					hostRecord.nativeWindow->SetCaretPoint(caretPoint);
				}
			}

			GuiGraphicsTimerManager* GuiGraphicsHost::GetTimerManager()
			{
				return &timerManager;
			}

			void GuiGraphicsHost::DisconnectComposition(GuiGraphicsComposition* composition)
			{
				DisconnectCompositionInternal(composition);
			}

/***********************************************************************
GuiShortcutKeyItem
***********************************************************************/

			GuiShortcutKeyItem::GuiShortcutKeyItem(GuiShortcutKeyManager* _shortcutKeyManager, bool _ctrl, bool _shift, bool _alt, vint _key)
				:shortcutKeyManager(_shortcutKeyManager)
				,ctrl(_ctrl)
				,shift(_shift)
				,alt(_alt)
				,key(_key)
			{
			}

			GuiShortcutKeyItem::~GuiShortcutKeyItem()
			{
			}

			IGuiShortcutKeyManager* GuiShortcutKeyItem::GetManager()
			{
				return shortcutKeyManager;
			}

			WString GuiShortcutKeyItem::GetName()
			{
				WString name;
				if(ctrl) name+=L"Ctrl+";
				if(shift) name+=L"Shift+";
				if(alt) name+=L"Alt+";
				name+=GetCurrentController()->InputService()->GetKeyName(key);
				return name;
			}

			bool GuiShortcutKeyItem::CanActivate(const NativeWindowKeyInfo& info)
			{
				return
					info.ctrl==ctrl &&
					info.shift==shift &&
					info.alt==alt &&
					info.code==key;
			}

			bool GuiShortcutKeyItem::CanActivate(bool _ctrl, bool _shift, bool _alt, vint _key)
			{
				return
					_ctrl==ctrl &&
					_shift==shift &&
					_alt==alt &&
					_key==key;
			}

/***********************************************************************
GuiShortcutKeyManager
***********************************************************************/

			GuiShortcutKeyManager::GuiShortcutKeyManager()
			{
			}

			GuiShortcutKeyManager::~GuiShortcutKeyManager()
			{
			}

			vint GuiShortcutKeyManager::GetItemCount()
			{
				return shortcutKeyItems.Count();
			}

			IGuiShortcutKeyItem* GuiShortcutKeyManager::GetItem(vint index)
			{
				return shortcutKeyItems[index].Obj();
			}

			bool GuiShortcutKeyManager::Execute(const NativeWindowKeyInfo& info)
			{
				bool executed=false;
				FOREACH(Ptr<GuiShortcutKeyItem>, item, shortcutKeyItems)
				{
					if(item->CanActivate(info))
					{
						GuiEventArgs arguments;
						item->Executed.Execute(arguments);
						executed=true;
					}
				}
				return executed;
			}

			IGuiShortcutKeyItem* GuiShortcutKeyManager::CreateShortcut(bool ctrl, bool shift, bool alt, vint key)
			{
				FOREACH(Ptr<GuiShortcutKeyItem>, item, shortcutKeyItems)
				{
					if(item->CanActivate(ctrl, shift, alt, key))
					{
						return item.Obj();
					}
				}
				Ptr<GuiShortcutKeyItem> item=new GuiShortcutKeyItem(this, ctrl, shift, alt, key);
				shortcutKeyItems.Add(item);
				return item.Obj();
			}

			bool GuiShortcutKeyManager::DestroyShortcut(bool ctrl, bool shift, bool alt, vint key)
			{
				FOREACH(Ptr<GuiShortcutKeyItem>, item, shortcutKeyItems)
				{
					if(item->CanActivate(ctrl, shift, alt, key))
					{
						shortcutKeyItems.Remove(item.Obj());
						return true;
					}
				}
				return false;
			}

			IGuiShortcutKeyItem* GuiShortcutKeyManager::TryGetShortcut(bool ctrl, bool shift, bool alt, vint key)
			{
				FOREACH(Ptr<GuiShortcutKeyItem>, item, shortcutKeyItems)
				{
					if(item->CanActivate(ctrl, shift, alt, key))
					{
						return item.Obj();
					}
				}
				return 0;
			}
		}
	}
}

/***********************************************************************
.\CONTROLS\GUIBUTTONCONTROLS.CPP
***********************************************************************/

namespace vl
{
	namespace presentation
	{
		namespace controls
		{
			using namespace elements;
			using namespace compositions;
			using namespace collections;
			using namespace reflection::description;

/***********************************************************************
GuiButton
***********************************************************************/

			void GuiButton::BeforeControlTemplateUninstalled_()
			{
			}

			void GuiButton::AfterControlTemplateInstalled_(bool initialize)
			{
				auto ct = GetControlTemplateObject(true);
				GetControlTemplateObject(true)->SetState(controlState);
			}

			void GuiButton::OnParentLineChanged()
			{
				GuiControl::OnParentLineChanged();
				if(GetRelatedControlHost()==0)
				{
					mousePressing=false;
					mouseHoving=false;
					UpdateControlState();
				}
			}

			void GuiButton::OnActiveAlt()
			{
				Clicked.Execute(GetNotifyEventArguments());
			}

			void GuiButton::UpdateControlState()
			{
				auto newControlState = ButtonState::Normal;
				if (mousePressing)
				{
					if (mouseHoving)
					{
						newControlState = ButtonState::Pressed;
					}
					else
					{
						newControlState = ButtonState::Active;
					}
				}
				else
				{
					if (mouseHoving)
					{
						newControlState = ButtonState::Active;
					}
					else
					{
						newControlState = ButtonState::Normal;
					}
				}
				if (controlState != newControlState)
				{
					controlState = newControlState;
					GetControlTemplateObject(true)->SetState(controlState);
				}
			}

			void GuiButton::OnLeftButtonDown(compositions::GuiGraphicsComposition* sender, compositions::GuiMouseEventArgs& arguments)
			{
				if(arguments.eventSource==boundsComposition)
				{
					mousePressing=true;
					boundsComposition->GetRelatedGraphicsHost()->SetFocus(boundsComposition);
					UpdateControlState();
					if(!clickOnMouseUp && arguments.eventSource->GetAssociatedControl()==this)
					{
						Clicked.Execute(GetNotifyEventArguments());
					}
				}
			}

			void GuiButton::OnLeftButtonUp(compositions::GuiGraphicsComposition* sender, compositions::GuiMouseEventArgs& arguments)
			{
				if(arguments.eventSource==boundsComposition)
				{
					mousePressing=false;
					UpdateControlState();
				}
				if(GetVisuallyEnabled())
				{
					if(mouseHoving && clickOnMouseUp)
					{
						auto eventSource = arguments.eventSource->GetAssociatedControl();
						while (eventSource && eventSource != this)
						{
							if (eventSource->GetFocusableComposition())
							{
								return;
							}
							eventSource = eventSource->GetParent();
						}
						Clicked.Execute(GetNotifyEventArguments());
					}
				}
			}

			void GuiButton::OnMouseEnter(compositions::GuiGraphicsComposition* sender, compositions::GuiEventArgs& arguments)
			{
				if(arguments.eventSource==boundsComposition)
				{
					mouseHoving=true;
					UpdateControlState();
				}
			}

			void GuiButton::OnMouseLeave(compositions::GuiGraphicsComposition* sender, compositions::GuiEventArgs& arguments)
			{
				if(arguments.eventSource==boundsComposition)
				{
					mouseHoving=false;
					UpdateControlState();
				}
			}

			GuiButton::GuiButton(theme::ThemeName themeName)
				:GuiControl(themeName)
			{
				Clicked.SetAssociatedComposition(boundsComposition);
				SetFocusableComposition(boundsComposition);

				boundsComposition->GetEventReceiver()->leftButtonDown.AttachMethod(this, &GuiButton::OnLeftButtonDown);
				boundsComposition->GetEventReceiver()->leftButtonUp.AttachMethod(this, &GuiButton::OnLeftButtonUp);
				boundsComposition->GetEventReceiver()->mouseEnter.AttachMethod(this, &GuiButton::OnMouseEnter);
				boundsComposition->GetEventReceiver()->mouseLeave.AttachMethod(this, &GuiButton::OnMouseLeave);
			}

			GuiButton::~GuiButton()
			{
			}

			bool GuiButton::GetClickOnMouseUp()
			{
				return clickOnMouseUp;
			}

			void GuiButton::SetClickOnMouseUp(bool value)
			{
				clickOnMouseUp=value;
			}

/***********************************************************************
GuiSelectableButton::GroupController
***********************************************************************/

			GuiSelectableButton::GroupController::GroupController()
			{
			}

			GuiSelectableButton::GroupController::~GroupController()
			{
				for(vint i=buttons.Count()-1;i>=0;i--)
				{
					buttons[i]->SetGroupController(0);
				}
			}

			void GuiSelectableButton::GroupController::Attach(GuiSelectableButton* button)
			{
				if(!buttons.Contains(button))
				{
					buttons.Add(button);
				}
			}

			void GuiSelectableButton::GroupController::Detach(GuiSelectableButton* button)
			{
				buttons.Remove(button);
			}

/***********************************************************************
GuiSelectableButton::MutexGroupController
***********************************************************************/

			GuiSelectableButton::MutexGroupController::MutexGroupController()
				:suppress(false)
			{
			}

			GuiSelectableButton::MutexGroupController::~MutexGroupController()
			{
			}

			void GuiSelectableButton::MutexGroupController::OnSelectedChanged(GuiSelectableButton* button)
			{
				if(!suppress)
				{
					suppress=true;
					for(vint i=0;i<buttons.Count();i++)
					{
						buttons[i]->SetSelected(buttons[i]==button);
					}
					suppress=false;
				}
			}

/***********************************************************************
GuiSelectableButton
***********************************************************************/

			void GuiSelectableButton::BeforeControlTemplateUninstalled_()
			{
			}

			void GuiSelectableButton::AfterControlTemplateInstalled_(bool initialize)
			{
				GetControlTemplateObject(true)->SetSelected(isSelected);
			}

			void GuiSelectableButton::OnClicked(compositions::GuiGraphicsComposition* sender, compositions::GuiEventArgs& arguments)
			{
				if(autoSelection)
				{
					SetSelected(!GetSelected());
				}
			}

			GuiSelectableButton::GuiSelectableButton(theme::ThemeName themeName)
				:GuiButton(themeName)
			{
				GroupControllerChanged.SetAssociatedComposition(boundsComposition);
				AutoSelectionChanged.SetAssociatedComposition(boundsComposition);
				SelectedChanged.SetAssociatedComposition(boundsComposition);

				Clicked.AttachMethod(this, &GuiSelectableButton::OnClicked);
			}
			
			GuiSelectableButton::~GuiSelectableButton()
			{
				if(groupController)
				{
					groupController->Detach(this);
				}
			}

			GuiSelectableButton::GroupController* GuiSelectableButton::GetGroupController()
			{
				return groupController;
			}

			void GuiSelectableButton::SetGroupController(GroupController* value)
			{
				if(groupController)
				{
					groupController->Detach(this);
				}
				groupController=value;
				if(groupController)
				{
					groupController->Attach(this);
				}
				GroupControllerChanged.Execute(GetNotifyEventArguments());
			}

			bool GuiSelectableButton::GetAutoSelection()
			{
				return autoSelection;
			}

			void GuiSelectableButton::SetAutoSelection(bool value)
			{
				if(autoSelection!=value)
				{
					autoSelection=value;
					AutoSelectionChanged.Execute(GetNotifyEventArguments());
				}
			}

			bool GuiSelectableButton::GetSelected()
			{
				return isSelected;
			}

			void GuiSelectableButton::SetSelected(bool value)
			{
				if (isSelected != value)
				{
					isSelected = value;
					GetControlTemplateObject(true)->SetSelected(isSelected);
					if (groupController)
					{
						groupController->OnSelectedChanged(this);
					}
					SelectedChanged.Execute(GetNotifyEventArguments());
				}
			}
		}
	}
}

/***********************************************************************
.\CONTROLS\GUICONTAINERCONTROLS.CPP
***********************************************************************/


namespace vl
{
	namespace presentation
	{
		using namespace compositions;

		namespace controls
		{

/***********************************************************************
GuiTabPage
***********************************************************************/

			bool GuiTabPage::IsAltAvailable()
			{
				return false;
			}

			GuiTabPage::GuiTabPage(theme::ThemeName themeName)
				:GuiCustomControl(themeName)
			{
			}

			GuiTabPage::~GuiTabPage()
			{
				FinalizeAggregation();
			}

			GuiTab* GuiTabPage::GetOwnerTab()
			{
				return tab;
			}

/***********************************************************************
GuiTabPageList
***********************************************************************/

			bool GuiTabPageList::QueryInsert(vint index, GuiTabPage* const& value)
			{
				return !items.Contains(value) && value->tab == nullptr;
			}

			void GuiTabPageList::AfterInsert(vint index, GuiTabPage* const& value)
			{
				value->tab = tab;
				value->SetVisible(false);
				value->boundsComposition->SetAlignmentToParent(Margin(0, 0, 0, 0));
				tab->containerComposition->AddChild(value->boundsComposition);

				if (!tab->selectedPage)
				{
					tab->SetSelectedPage(value);
				}
			}

			void GuiTabPageList::BeforeRemove(vint index, GuiTabPage* const& value)
			{
				tab->containerComposition->RemoveChild(value->boundsComposition);
				value->tab = nullptr;

				if (items.Count() == 0)
				{
					tab->SetSelectedPage(nullptr);
				}
				else if (tab->selectedPage == value)
				{
					tab->SetSelectedPage(items[0]);
				}
			}

			GuiTabPageList::GuiTabPageList(GuiTab* _tab)
				:tab(_tab)
			{
			}

			GuiTabPageList::~GuiTabPageList()
			{
			}

/***********************************************************************
GuiTab
***********************************************************************/

			void GuiTab::BeforeControlTemplateUninstalled_()
			{
				auto ct = GetControlTemplateObject(false);
				if (!ct) return;

				ct->SetCommands(nullptr);
				ct->SetTabPages(nullptr);
				ct->SetSelectedTabPage(nullptr);
			}

			void GuiTab::AfterControlTemplateInstalled_(bool initialize)
			{
				auto ct = GetControlTemplateObject(true);
				ct->SetCommands(commandExecutor.Obj());
				ct->SetTabPages(tabPages.GetWrapper());
				ct->SetSelectedTabPage(selectedPage);
			}

			GuiTab::CommandExecutor::CommandExecutor(GuiTab* _tab)
				:tab(_tab)
			{
			}

			GuiTab::CommandExecutor::~CommandExecutor()
			{
			}

			void GuiTab::CommandExecutor::ShowTab(vint index)
			{
				tab->SetSelectedPage(tab->GetPages().Get(index));
			}

			GuiTab::GuiTab(theme::ThemeName themeName)
				:GuiControl(themeName)
				, tabPages(this)
			{
				commandExecutor = new CommandExecutor(this);
			}

			GuiTab::~GuiTab()
			{
			}

			collections::ObservableList<GuiTabPage*>& GuiTab::GetPages()
			{
				return tabPages;
			}

			GuiTabPage* GuiTab::GetSelectedPage()
			{
				return selectedPage;
			}

			bool GuiTab::SetSelectedPage(GuiTabPage* value)
			{
				if (!value)
				{
					if (tabPages.Count() == 0)
					{
						selectedPage = nullptr;
					}
				}
				else if (value->GetOwnerTab() == this)
				{
					if (selectedPage == value)
					{
						return true;
					}

					selectedPage = value;
					FOREACH(GuiTabPage*, tabPage, tabPages)
					{
						tabPage->SetVisible(tabPage == selectedPage);
					}
				}
				if (auto ct = GetControlTemplateObject(false))
				{
					ct->SetSelectedTabPage(selectedPage);
				}
				SelectedPageChanged.Execute(GetNotifyEventArguments());
				return selectedPage == value;
			}

/***********************************************************************
GuiScrollView
***********************************************************************/

			void GuiScrollView::BeforeControlTemplateUninstalled_()
			{
				auto ct = GetControlTemplateObject(false);
				if (!ct) return;

				if (auto scroll = ct->GetHorizontalScroll())
				{
					scroll->PositionChanged.Detach(hScrollHandler);
				}
				if (auto scroll = ct->GetVerticalScroll())
				{
					scroll->PositionChanged.Detach(vScrollHandler);
				}
				ct->GetEventReceiver()->horizontalWheel.Detach(hWheelHandler);
				ct->GetEventReceiver()->verticalWheel.Detach(vWheelHandler);
				ct->BoundsChanged.Detach(containerBoundsChangedHandler);

				hScrollHandler = nullptr;
				vScrollHandler = nullptr;
				hWheelHandler = nullptr;
				vWheelHandler = nullptr;
				containerBoundsChangedHandler = nullptr;
				supressScrolling = false;
			}

			void GuiScrollView::AfterControlTemplateInstalled_(bool initialize)
			{
				auto ct = GetControlTemplateObject(true);
				if (auto scroll = ct->GetHorizontalScroll())
				{
					hScrollHandler = scroll->PositionChanged.AttachMethod(this, &GuiScrollView::OnHorizontalScroll);
				}
				if (auto scroll = ct->GetVerticalScroll())
				{
					vScrollHandler = scroll->PositionChanged.AttachMethod(this, &GuiScrollView::OnVerticalScroll);
				}
				hWheelHandler = ct->GetEventReceiver()->horizontalWheel.AttachMethod(this, &GuiScrollView::OnHorizontalWheel);
				vWheelHandler = ct->GetEventReceiver()->verticalWheel.AttachMethod(this, &GuiScrollView::OnVerticalWheel);
				containerBoundsChangedHandler = ct->BoundsChanged.AttachMethod(this, &GuiScrollView::OnContainerBoundsChanged);
				CalculateView();
			}

			void GuiScrollView::OnContainerBoundsChanged(compositions::GuiGraphicsComposition* sender, compositions::GuiEventArgs& arguments)
			{
				CalculateView();
			}

			void GuiScrollView::OnHorizontalScroll(compositions::GuiGraphicsComposition* sender, compositions::GuiEventArgs& arguments)
			{
				if(!supressScrolling)
				{
					CallUpdateView();
				}
			}

			void GuiScrollView::OnVerticalScroll(compositions::GuiGraphicsComposition* sender, compositions::GuiEventArgs& arguments)
			{
				if(!supressScrolling)
				{
					CallUpdateView();
				}
			}

			void GuiScrollView::OnHorizontalWheel(compositions::GuiGraphicsComposition* sender, compositions::GuiMouseEventArgs& arguments)
			{
				if(!supressScrolling)
				{
					if (auto scroll = GetControlTemplateObject(true)->GetHorizontalScroll())
					{
						vint position = scroll->GetPosition();
						vint move = scroll->GetSmallMove();
						position -= move * arguments.wheel / 60;
						scroll->SetPosition(position);
					}
				}
			}

			void GuiScrollView::OnVerticalWheel(compositions::GuiGraphicsComposition* sender, compositions::GuiMouseEventArgs& arguments)
			{
				if(!supressScrolling && GetVisuallyEnabled())
				{
					if (auto scroll = GetControlTemplateObject(true)->GetVerticalScroll())
					{
						vint position = scroll->GetPosition();
						vint move = scroll->GetSmallMove();
						position -= move * arguments.wheel / 60;
						scroll->SetPosition(position);
					}
				}
			}

			void GuiScrollView::CallUpdateView()
			{
				Rect viewBounds=GetViewBounds();
				UpdateView(viewBounds);
			}

			bool GuiScrollView::AdjustView(Size fullSize)
			{
				auto ct = GetControlTemplateObject(true);
				auto hScroll = ct->GetHorizontalScroll();
				auto vScroll = ct->GetVerticalScroll();
				Size viewSize = ct->GetContainerComposition()->GetBounds().GetSize();

				auto hVisible = hScroll ? hScroll->GetVisible() : false;
				auto vVisible = vScroll ? vScroll->GetVisible() : false;

				if (hScroll)
				{
					if (fullSize.x <= viewSize.x)
					{
						hScroll->SetVisible(horizontalAlwaysVisible);
						hScroll->SetEnabled(false);
						hScroll->SetPosition(0);
					}
					else
					{
						hScroll->SetVisible(true);
						hScroll->SetEnabled(true);
						hScroll->SetTotalSize(fullSize.x);
						hScroll->SetPageSize(viewSize.x);
					}
				}

				if (vScroll)
				{
					if (fullSize.y <= viewSize.y)
					{
						vScroll->SetVisible(verticalAlwaysVisible);
						vScroll->SetEnabled(false);
						vScroll->SetPosition(0);
					}
					else
					{
						vScroll->SetVisible(true);
						vScroll->SetEnabled(true);
						vScroll->SetTotalSize(fullSize.y);
						vScroll->SetPageSize(viewSize.y);
					}
				}

				auto hVisible2 = hScroll ? hScroll->GetVisible() : false;
				auto vVisible2 = vScroll ? vScroll->GetVisible() : false;
				return hVisible != hVisible2 || vVisible != vVisible2;
			}

			GuiScrollView::GuiScrollView(theme::ThemeName themeName)
				:GuiControl(themeName)
			{
				containerComposition->BoundsChanged.AttachMethod(this, &GuiScrollView::OnContainerBoundsChanged);
			}

			vint GuiScrollView::GetSmallMove()
			{
				return GetFont().size * 2;
			}

			Size GuiScrollView::GetBigMove()
			{
				return GetViewSize();
			}
			
			GuiScrollView::~GuiScrollView()
			{
			}

			void GuiScrollView::SetFont(const FontProperties& value)
			{
				GuiControl::SetFont(value);
				CalculateView();
			}

			void GuiScrollView::CalculateView()
			{
				auto ct = GetControlTemplateObject(true);
				if (!supressScrolling)
				{
					Size fullSize = QueryFullSize();
					while (true)
					{
						bool flagA = AdjustView(fullSize);
						bool flagB = AdjustView(fullSize);
						supressScrolling = true;
						CallUpdateView();
						supressScrolling = false;

						Size newSize = QueryFullSize();
						if (fullSize == newSize)
						{
							vint smallMove = GetSmallMove();
							Size bigMove = GetBigMove();
							if (auto scroll = ct->GetHorizontalScroll())
							{
								scroll->SetSmallMove(smallMove);
								scroll->SetBigMove(bigMove.x);
							}
							if (auto scroll = ct->GetVerticalScroll())
							{
								scroll->SetSmallMove(smallMove);
								scroll->SetBigMove(bigMove.y);
							}

							if (!flagA && !flagB)
							{
								break;
							}
						}
						else
						{
							fullSize = newSize;
						}
					}
				}
			}

			Size GuiScrollView::GetViewSize()
			{
				Size viewSize = GetControlTemplateObject(true)->GetContainerComposition()->GetBounds().GetSize();
				return viewSize;
			}

			Rect GuiScrollView::GetViewBounds()
			{
				return Rect(GetViewPosition(), GetViewSize());
			}

			Point GuiScrollView::GetViewPosition()
			{
				auto ct = GetControlTemplateObject(true);
				auto hScroll = ct->GetHorizontalScroll();
				auto vScroll = ct->GetVerticalScroll();
				return Point(hScroll ? hScroll->GetPosition() : 0, vScroll ? vScroll->GetPosition() : 0);
			}

			void GuiScrollView::SetViewPosition(Point value)
			{
				auto ct = GetControlTemplateObject(true);
				if (auto hScroll = ct->GetHorizontalScroll())
				{
					hScroll->SetPosition(value.x);
				}
				if (auto vScroll = ct->GetVerticalScroll())
				{
					vScroll->SetPosition(value.y);
				}
			}

			GuiScroll* GuiScrollView::GetHorizontalScroll()
			{
				return GetControlTemplateObject(true)->GetHorizontalScroll();
			}

			GuiScroll* GuiScrollView::GetVerticalScroll()
			{
				return GetControlTemplateObject(true)->GetVerticalScroll();
			}

			bool GuiScrollView::GetHorizontalAlwaysVisible()
			{
				return horizontalAlwaysVisible;
			}

			void GuiScrollView::SetHorizontalAlwaysVisible(bool value)
			{
				if (horizontalAlwaysVisible != value)
				{
					horizontalAlwaysVisible = value;
					CalculateView();
				}
			}

			bool GuiScrollView::GetVerticalAlwaysVisible()
			{
				return verticalAlwaysVisible;
			}

			void GuiScrollView::SetVerticalAlwaysVisible(bool value)
			{
				if (verticalAlwaysVisible != value)
				{
					verticalAlwaysVisible = value;
					CalculateView();
				}
			}

/***********************************************************************
GuiScrollContainer
***********************************************************************/

			Size GuiScrollContainer::QueryFullSize()
			{
				return containerComposition->GetBounds().GetSize();
			}

			void GuiScrollContainer::UpdateView(Rect viewBounds)
			{
				auto leftTop = Point(-viewBounds.x1, -viewBounds.y1);
				containerComposition->SetBounds(Rect(leftTop, Size(0, 0)));
			}

			GuiScrollContainer::GuiScrollContainer(theme::ThemeName themeName)
				:GuiScrollView(themeName)
			{
				containerComposition->SetAlignmentToParent(Margin(-1, -1, -1, -1));
				UpdateView(Rect(0, 0, 0, 0));
			}

			GuiScrollContainer::~GuiScrollContainer()
			{
			}

			bool GuiScrollContainer::GetExtendToFullWidth()
			{
				return extendToFullWidth;
			}

			void GuiScrollContainer::SetExtendToFullWidth(bool value)
			{
				if (extendToFullWidth != value)
				{
					extendToFullWidth = value;
					auto margin = containerComposition->GetAlignmentToParent();
					if (value)
					{
						containerComposition->SetAlignmentToParent(Margin(0, margin.top, 0, margin.bottom));
					}
					else
					{
						containerComposition->SetAlignmentToParent(Margin(-1, margin.top, -1, margin.bottom));
					}
				}
			}

			bool GuiScrollContainer::GetExtendToFullHeight()
			{
				return extendToFullHeight;
			}

			void GuiScrollContainer::SetExtendToFullHeight(bool value)
			{
				if (extendToFullHeight != value)
				{
					extendToFullHeight = value;
					auto margin = containerComposition->GetAlignmentToParent();
					if (value)
					{
						containerComposition->SetAlignmentToParent(Margin(margin.left, 0, margin.right, 0));
					}
					else
					{
						containerComposition->SetAlignmentToParent(Margin(margin.left, -1, margin.right, -1));
					}
				}
			}
		}
	}
}

/***********************************************************************
.\CONTROLS\GUISCROLLCONTROLS.CPP
***********************************************************************/

namespace vl
{
	namespace presentation
	{
		namespace controls
		{
			using namespace elements;
			using namespace compositions;
			using namespace collections;
			using namespace reflection::description;

/***********************************************************************
GuiScroll::CommandExecutor
***********************************************************************/

			GuiScroll::CommandExecutor::CommandExecutor(GuiScroll* _scroll)
				:scroll(_scroll)
			{
			}

			GuiScroll::CommandExecutor::~CommandExecutor()
			{
			}

			void GuiScroll::CommandExecutor::SmallDecrease()
			{
				scroll->SetPosition(scroll->GetPosition()-scroll->GetSmallMove());
			}

			void GuiScroll::CommandExecutor::SmallIncrease()
			{
				scroll->SetPosition(scroll->GetPosition()+scroll->GetSmallMove());
			}

			void GuiScroll::CommandExecutor::BigDecrease()
			{
				scroll->SetPosition(scroll->GetPosition()-scroll->GetBigMove());
			}

			void GuiScroll::CommandExecutor::BigIncrease()
			{
				scroll->SetPosition(scroll->GetPosition()+scroll->GetBigMove());
			}
			
			void GuiScroll::CommandExecutor::SetTotalSize(vint value)
			{
				scroll->SetTotalSize(value);
			}

			void GuiScroll::CommandExecutor::SetPageSize(vint value)
			{
				scroll->SetPageSize(value);
			}

			void GuiScroll::CommandExecutor::SetPosition(vint value)
			{
				scroll->SetPosition(value);
			}

/***********************************************************************
GuiScroll
***********************************************************************/

			void GuiScroll::BeforeControlTemplateUninstalled_()
			{
				auto ct = GetControlTemplateObject(false);
				if (!ct) return;

				ct->SetCommands(nullptr);
			}

			void GuiScroll::AfterControlTemplateInstalled_(bool initialize)
			{
				auto ct = GetControlTemplateObject(true);
				ct->SetCommands(commandExecutor.Obj());
				ct->SetPageSize(pageSize);
				ct->SetTotalSize(totalSize);
				ct->SetPosition(position);
			}

			GuiScroll::GuiScroll(theme::ThemeName themeName)
				:GuiControl(themeName)
			{
				TotalSizeChanged.SetAssociatedComposition(boundsComposition);
				PageSizeChanged.SetAssociatedComposition(boundsComposition);
				PositionChanged.SetAssociatedComposition(boundsComposition);
				SmallMoveChanged.SetAssociatedComposition(boundsComposition);
				BigMoveChanged.SetAssociatedComposition(boundsComposition);

				commandExecutor = new CommandExecutor(this);
			}

			GuiScroll::~GuiScroll()
			{
			}

			vint GuiScroll::GetTotalSize()
			{
				return totalSize;
			}

			void GuiScroll::SetTotalSize(vint value)
			{
				if(totalSize!=value && 0<value)
				{
					totalSize=value;
					if(pageSize>totalSize)
					{
						SetPageSize(totalSize);
					}
					if(position>GetMaxPosition())
					{
						SetPosition(GetMaxPosition());
					}
					GetControlTemplateObject(true)->SetTotalSize(totalSize);
					TotalSizeChanged.Execute(GetNotifyEventArguments());
				}
			}

			vint GuiScroll::GetPageSize()
			{
				return pageSize;
			}

			void GuiScroll::SetPageSize(vint value)
			{
				if(pageSize!=value && 0<=value && value<=totalSize)
				{
					pageSize=value;
					if(position>GetMaxPosition())
					{
						SetPosition(GetMaxPosition());
					}
					GetControlTemplateObject(true)->SetPageSize(pageSize);
					PageSizeChanged.Execute(GetNotifyEventArguments());
				}
			}

			vint GuiScroll::GetPosition()
			{
				return position;
			}

			void GuiScroll::SetPosition(vint value)
			{
				vint min=GetMinPosition();
				vint max=GetMaxPosition();
				vint newPosition=
					value<min?min:
					value>max?max:
					value;
				if(position!=newPosition)
				{
					position=newPosition;
					GetControlTemplateObject(true)->SetPosition(position);
					PositionChanged.Execute(GetNotifyEventArguments());
				}
			}

			vint GuiScroll::GetSmallMove()
			{
				return smallMove;
			}

			void GuiScroll::SetSmallMove(vint value)
			{
				if(value>0 && smallMove!=value)
				{
					smallMove=value;
					SmallMoveChanged.Execute(GetNotifyEventArguments());
				}
			}

			vint GuiScroll::GetBigMove()
			{
				return bigMove;
			}

			void GuiScroll::SetBigMove(vint value)
			{
				if(value>0 && bigMove!=value)
				{
					bigMove=value;
					BigMoveChanged.Execute(GetNotifyEventArguments());
				}
			}

			vint GuiScroll::GetMinPosition()
			{
				return 0;
			}

			vint GuiScroll::GetMaxPosition()
			{
				return totalSize-pageSize;
			}
		}
	}
}

/***********************************************************************
.\CONTROLS\LISTCONTROLPACKAGE\GUILISTCONTROLS.CPP
***********************************************************************/

namespace vl
{
	namespace presentation
	{
		namespace controls
		{
			using namespace collections;
			using namespace elements;
			using namespace compositions;

/***********************************************************************
GuiListControl::ItemCallback
***********************************************************************/

			Ptr<GuiListControl::ItemCallback::BoundsChangedHandler> GuiListControl::ItemCallback::InstallStyle(ItemStyle* style, vint itemIndex, compositions::GuiBoundsComposition* itemComposition)
			{
				auto handler = style->BoundsChanged.AttachMethod(this, &ItemCallback::OnStyleBoundsChanged);
				listControl->GetContainerComposition()->AddChild(itemComposition ? itemComposition : style);
				listControl->OnStyleInstalled(itemIndex, style);
				return handler;
			}

			GuiListControl::ItemStyle* GuiListControl::ItemCallback::UninstallStyle(vint index)
			{
				auto style = installedStyles.Keys()[index];
				auto handler = installedStyles.Values()[index];
				listControl->OnStyleUninstalled(style);
				listControl->GetContainerComposition()->RemoveChild(style);
				style->BoundsChanged.Detach(handler);
				return style;
			}

			void GuiListControl::ItemCallback::OnStyleBoundsChanged(compositions::GuiGraphicsComposition* sender, compositions::GuiEventArgs& arguments)
			{
				listControl->CalculateView();
			}

			GuiListControl::ItemCallback::ItemCallback(GuiListControl* _listControl)
				:listControl(_listControl)
			{
			}

			GuiListControl::ItemCallback::~ItemCallback()
			{
				ClearCache();
			}

			void GuiListControl::ItemCallback::ClearCache()
			{
				for (vint i = 0; i < installedStyles.Count(); i++)
				{
					auto style = UninstallStyle(i);
					SafeDeleteComposition(style);
				}
				installedStyles.Clear();
			}

			void GuiListControl::ItemCallback::OnAttached(IItemProvider* provider)
			{
				itemProvider = provider;
			}

			void GuiListControl::ItemCallback::OnItemModified(vint start, vint count, vint newCount)
			{
				listControl->OnItemModified(start, count, newCount);
			}

			GuiListControl::ItemStyle* GuiListControl::ItemCallback::RequestItem(vint itemIndex, compositions::GuiBoundsComposition* itemComposition)
			{
				CHECK_ERROR(0 <= itemIndex && itemIndex < itemProvider->Count(), L"GuiListControl::ItemCallback::RequestItem(vint)#Index out of range.");
				CHECK_ERROR(listControl->itemStyleProperty, L"GuiListControl::ItemCallback::RequestItem(vint)#SetItemTemplate function should be called before adding items to the list control.");

				auto style = listControl->itemStyleProperty(itemProvider->GetBindingValue(itemIndex));
				auto handler = InstallStyle(style, itemIndex, itemComposition);
				installedStyles.Add(style, handler);
				return style;
			}

			void GuiListControl::ItemCallback::ReleaseItem(ItemStyle* style)
			{
				vint index = installedStyles.Keys().IndexOf(style);
				if (index != -1)
				{
					auto style = UninstallStyle(index);
					installedStyles.Remove(style);
					SafeDeleteComposition(style);
				}
			}

			void GuiListControl::ItemCallback::SetViewLocation(Point value)
			{
				Rect virtualRect(value, listControl->GetViewSize());
				Rect realRect = listControl->axis->VirtualRectToRealRect(listControl->fullSize, virtualRect);
				listControl->SetViewPosition(realRect.LeftTop());
			}

			Size GuiListControl::ItemCallback::GetStylePreferredSize(compositions::GuiBoundsComposition* style)
			{
				Size size = style->GetPreferredBounds().GetSize();
				return listControl->axis->RealSizeToVirtualSize(size);
			}

			void GuiListControl::ItemCallback::SetStyleAlignmentToParent(compositions::GuiBoundsComposition* style, Margin margin)
			{
				Margin newMargin = listControl->axis->VirtualMarginToRealMargin(margin);
				style->SetAlignmentToParent(newMargin);
			}

			Rect GuiListControl::ItemCallback::GetStyleBounds(compositions::GuiBoundsComposition* style)
			{
				Rect bounds = style->GetBounds();
				return listControl->axis->RealRectToVirtualRect(listControl->GetViewSize(), bounds);
			}

			void GuiListControl::ItemCallback::SetStyleBounds(compositions::GuiBoundsComposition* style, Rect bounds)
			{
				Rect newBounds = listControl->axis->VirtualRectToRealRect(listControl->GetViewSize(), bounds);
				return style->SetBounds(newBounds);
			}

			compositions::GuiGraphicsComposition* GuiListControl::ItemCallback::GetContainerComposition()
			{
				return listControl->GetContainerComposition();
			}

			void GuiListControl::ItemCallback::OnTotalSizeChanged()
			{
				listControl->CalculateView();
			}

/***********************************************************************
GuiListControl
***********************************************************************/

			void GuiListControl::BeforeControlTemplateUninstalled_()
			{
			}

			void GuiListControl::AfterControlTemplateInstalled_(bool initialize)
			{
				if (itemArranger)
				{
					itemArranger->ReloadVisibleStyles();
					CalculateView();
				}
			}

			void GuiListControl::OnItemModified(vint start, vint count, vint newCount)
			{
			}

			void GuiListControl::OnStyleInstalled(vint itemIndex, ItemStyle* style)
			{
				style->SetFont(GetFont());
				style->SetContext(GetContext());
				style->SetText(itemProvider->GetTextValue(itemIndex));
				style->SetVisuallyEnabled(GetVisuallyEnabled());
				style->SetSelected(false);
				style->SetIndex(itemIndex);
				style->Initialize(this);
				AttachItemEvents(style);
			}

			void GuiListControl::OnStyleUninstalled(ItemStyle* style)
			{
				DetachItemEvents(style);
			}

			void GuiListControl::OnRenderTargetChanged(elements::IGuiGraphicsRenderTarget* renderTarget)
			{
				SetStyleAndArranger(itemStyleProperty, itemArranger);
				GuiScrollView::OnRenderTargetChanged(renderTarget);
			}

			void GuiListControl::OnBeforeReleaseGraphicsHost()
			{
				GuiScrollView::OnBeforeReleaseGraphicsHost();
				SetStyleAndArranger({}, nullptr);
			}

			Size GuiListControl::QueryFullSize()
			{
				Size virtualSize = itemArranger ? itemArranger->GetTotalSize() : Size(0, 0);
				fullSize = axis->VirtualSizeToRealSize(virtualSize);
				return fullSize;
			}

			void GuiListControl::UpdateView(Rect viewBounds)
			{
				if (itemArranger)
				{
					Rect newBounds = axis->RealRectToVirtualRect(fullSize, viewBounds);
					itemArranger->OnViewChanged(newBounds);
				}
			}

			void GuiListControl::OnBoundsMouseButtonDown(compositions::GuiGraphicsComposition* sender, compositions::GuiMouseEventArgs& arguments)
			{
				if(GetVisuallyEnabled())
				{
					boundsComposition->GetRelatedGraphicsHost()->SetFocus(boundsComposition);
				}
			}

			void GuiListControl::SetStyleAndArranger(ItemStyleProperty styleProperty, Ptr<IItemArranger> arranger)
			{
				if (itemArranger)
				{
					itemArranger->DetachListControl();
					itemArranger->SetCallback(nullptr);
					itemProvider->DetachCallback(itemArranger.Obj());
				}
				callback->ClearCache();

				itemStyleProperty = styleProperty;
				itemArranger = arranger;

				if (auto scroll = GetVerticalScroll())
				{
					scroll->SetPosition(0);
				}
				if (auto scroll = GetHorizontalScroll())
				{
					scroll->SetPosition(0);
				}

				if (itemArranger)
				{
					itemProvider->AttachCallback(itemArranger.Obj());
					itemArranger->SetCallback(callback.Obj());
					itemArranger->AttachListControl(this);
				}
				CalculateView();
			}

			void GuiListControl::OnClientBoundsChanged(compositions::GuiGraphicsComposition* sender, compositions::GuiEventArgs& arguments)
			{
				auto args = GetNotifyEventArguments();
				AdoptedSizeInvalidated.Execute(args);
			}

			void GuiListControl::OnVisuallyEnabledChanged(compositions::GuiGraphicsComposition* sender, compositions::GuiEventArgs& arguments)
			{
				FOREACH(ItemStyle*, style, visibleStyles.Keys())
				{
					style->SetVisuallyEnabled(GetVisuallyEnabled());
				}
			}

			void GuiListControl::OnFontChanged(compositions::GuiGraphicsComposition* sender, compositions::GuiEventArgs& arguments)
			{
				FOREACH(ItemStyle*, style, visibleStyles.Keys())
				{
					style->SetFont(GetFont());
				}
			}

			void GuiListControl::OnContextChanged(compositions::GuiGraphicsComposition* sender, compositions::GuiEventArgs& arguments)
			{
				FOREACH(ItemStyle*, style, visibleStyles.Keys())
				{
					style->SetContext(GetContext());
				}
			}

			void GuiListControl::OnItemMouseEvent(compositions::GuiItemMouseEvent& itemEvent, ItemStyle* style, compositions::GuiGraphicsComposition* sender, compositions::GuiMouseEventArgs& arguments)
			{
				if (itemArranger && GetVisuallyEnabled())
				{
					vint itemIndex = itemArranger->GetVisibleIndex(style);
					if (itemIndex != -1)
					{
						GuiItemMouseEventArgs redirectArguments;
						(GuiMouseEventArgs&)redirectArguments = arguments;
						redirectArguments.compositionSource = boundsComposition;
						redirectArguments.eventSource = boundsComposition;
						redirectArguments.itemIndex = itemIndex;
						itemEvent.Execute(redirectArguments);
						arguments = redirectArguments;
					}
				}
			}

			void GuiListControl::OnItemNotifyEvent(compositions::GuiItemNotifyEvent& itemEvent, ItemStyle* style, compositions::GuiGraphicsComposition* sender, compositions::GuiEventArgs& arguments)
			{
				if (itemArranger && GetVisuallyEnabled())
				{
					vint itemIndex = itemArranger->GetVisibleIndex(style);
					if (itemIndex != -1)
					{
						GuiItemEventArgs redirectArguments;
						(GuiEventArgs&)redirectArguments = arguments;
						redirectArguments.compositionSource = boundsComposition;
						redirectArguments.eventSource = boundsComposition;
						redirectArguments.itemIndex = itemIndex;
						itemEvent.Execute(redirectArguments);
						arguments = redirectArguments;
					}
				}
			}

#define ATTACH_ITEM_MOUSE_EVENT(EVENTNAME, ITEMEVENTNAME)\
					{\
						Func<void(GuiItemMouseEvent&, ItemStyle*, GuiGraphicsComposition*, GuiMouseEventArgs&)> func(this, &GuiListControl::OnItemMouseEvent);\
						helper->EVENTNAME##Handler = style->GetEventReceiver()->EVENTNAME.AttachFunction(\
							Curry(Curry(func)(ITEMEVENTNAME))(style)\
							);\
					}\

#define ATTACH_ITEM_NOTIFY_EVENT(EVENTNAME, ITEMEVENTNAME)\
					{\
						Func<void(GuiItemNotifyEvent&, ItemStyle*, GuiGraphicsComposition*, GuiEventArgs&)> func(this, &GuiListControl::OnItemNotifyEvent);\
						helper->EVENTNAME##Handler = style->GetEventReceiver()->EVENTNAME.AttachFunction(\
							Curry(Curry(func)(ITEMEVENTNAME))(style)\
							);\
					}\

			void GuiListControl::AttachItemEvents(ItemStyle* style)
			{
				vint index=visibleStyles.Keys().IndexOf(style);
				if(index==-1)
				{
					Ptr<VisibleStyleHelper> helper=new VisibleStyleHelper;
					visibleStyles.Add(style, helper);

					ATTACH_ITEM_MOUSE_EVENT(leftButtonDown, ItemLeftButtonDown);
					ATTACH_ITEM_MOUSE_EVENT(leftButtonUp, ItemLeftButtonUp);
					ATTACH_ITEM_MOUSE_EVENT(leftButtonDoubleClick, ItemLeftButtonDoubleClick);
					ATTACH_ITEM_MOUSE_EVENT(middleButtonDown, ItemMiddleButtonDown);
					ATTACH_ITEM_MOUSE_EVENT(middleButtonUp, ItemMiddleButtonUp);
					ATTACH_ITEM_MOUSE_EVENT(middleButtonDoubleClick, ItemMiddleButtonDoubleClick);
					ATTACH_ITEM_MOUSE_EVENT(rightButtonDown, ItemRightButtonDown);
					ATTACH_ITEM_MOUSE_EVENT(rightButtonUp, ItemRightButtonUp);
					ATTACH_ITEM_MOUSE_EVENT(rightButtonDoubleClick, ItemRightButtonDoubleClick);
					ATTACH_ITEM_MOUSE_EVENT(mouseMove, ItemMouseMove);
					ATTACH_ITEM_NOTIFY_EVENT(mouseEnter, ItemMouseEnter);
					ATTACH_ITEM_NOTIFY_EVENT(mouseLeave, ItemMouseLeave);
				}
			}

#undef ATTACH_ITEM_MOUSE_EVENT
#undef ATTACH_ITEM_NOTIFY_EVENT

#define DETACH_ITEM_EVENT(EVENTNAME) style->GetEventReceiver()->EVENTNAME.Detach(helper->EVENTNAME##Handler)

			void GuiListControl::DetachItemEvents(ItemStyle* style)
			{
				vint index=visibleStyles.Keys().IndexOf(style);
				if(index!=-1)
				{
					Ptr<VisibleStyleHelper> helper=visibleStyles.Values().Get(index);
					visibleStyles.Remove(style);
					
					DETACH_ITEM_EVENT(leftButtonDown);
					DETACH_ITEM_EVENT(leftButtonUp);
					DETACH_ITEM_EVENT(leftButtonDoubleClick);
					DETACH_ITEM_EVENT(middleButtonDown);
					DETACH_ITEM_EVENT(middleButtonUp);
					DETACH_ITEM_EVENT(middleButtonDoubleClick);
					DETACH_ITEM_EVENT(rightButtonDown);
					DETACH_ITEM_EVENT(rightButtonUp);
					DETACH_ITEM_EVENT(rightButtonDoubleClick);
					DETACH_ITEM_EVENT(mouseMove);
					DETACH_ITEM_EVENT(mouseEnter);
					DETACH_ITEM_EVENT(mouseLeave);
				}
			}

#undef DETACH_ITEM_EVENT

			GuiListControl::GuiListControl(theme::ThemeName themeName, IItemProvider* _itemProvider, bool acceptFocus)
				:GuiScrollView(themeName)
				, itemProvider(_itemProvider)
			{
				FontChanged.AttachMethod(this, &GuiListControl::OnFontChanged);
				ContextChanged.AttachMethod(this, &GuiListControl::OnContextChanged);
				VisuallyEnabledChanged.AttachMethod(this, &GuiListControl::OnVisuallyEnabledChanged);
				containerComposition->BoundsChanged.AttachMethod(this, &GuiListControl::OnClientBoundsChanged);

				ItemTemplateChanged.SetAssociatedComposition(boundsComposition);
				ArrangerChanged.SetAssociatedComposition(boundsComposition);
				AxisChanged.SetAssociatedComposition(boundsComposition);
				AdoptedSizeInvalidated.SetAssociatedComposition(boundsComposition);

				ItemLeftButtonDown.SetAssociatedComposition(boundsComposition);
				ItemLeftButtonUp.SetAssociatedComposition(boundsComposition);
				ItemLeftButtonDoubleClick.SetAssociatedComposition(boundsComposition);
				ItemMiddleButtonDown.SetAssociatedComposition(boundsComposition);
				ItemMiddleButtonUp.SetAssociatedComposition(boundsComposition);
				ItemMiddleButtonDoubleClick.SetAssociatedComposition(boundsComposition);
				ItemRightButtonDown.SetAssociatedComposition(boundsComposition);
				ItemRightButtonUp.SetAssociatedComposition(boundsComposition);
				ItemRightButtonDoubleClick.SetAssociatedComposition(boundsComposition);
				ItemMouseMove.SetAssociatedComposition(boundsComposition);
				ItemMouseEnter.SetAssociatedComposition(boundsComposition);
				ItemMouseLeave.SetAssociatedComposition(boundsComposition);

				callback = new ItemCallback(this);
				itemProvider->AttachCallback(callback.Obj());
				axis = new GuiDefaultAxis;

				if (acceptFocus)
				{
					boundsComposition->GetEventReceiver()->leftButtonDown.AttachMethod(this, &GuiListControl::OnBoundsMouseButtonDown);
					boundsComposition->GetEventReceiver()->middleButtonDown.AttachMethod(this, &GuiListControl::OnBoundsMouseButtonDown);
					boundsComposition->GetEventReceiver()->rightButtonDown.AttachMethod(this, &GuiListControl::OnBoundsMouseButtonDown);
					SetFocusableComposition(boundsComposition);
				}
			}

			GuiListControl::~GuiListControl()
			{
				if(itemArranger)
				{
					itemProvider->DetachCallback(itemArranger.Obj());
				}
				callback->ClearCache();
				itemStyleProperty = {};
				itemArranger = nullptr;
			}

			GuiListControl::IItemProvider* GuiListControl::GetItemProvider()
			{
				return itemProvider.Obj();
			}

			GuiListControl::ItemStyleProperty GuiListControl::GetItemTemplate()
			{
				return itemStyleProperty;
			}

			void GuiListControl::SetItemTemplate(ItemStyleProperty value)
			{
				SetStyleAndArranger(value, itemArranger);
				ItemTemplateChanged.Execute(GetNotifyEventArguments());
			}

			GuiListControl::IItemArranger* GuiListControl::GetArranger()
			{
				return itemArranger.Obj();
			}

			void GuiListControl::SetArranger(Ptr<IItemArranger> value)
			{
				SetStyleAndArranger(itemStyleProperty, value);
				ArrangerChanged.Execute(GetNotifyEventArguments());
			}

			compositions::IGuiAxis* GuiListControl::GetAxis()
			{
				return axis.Obj();
			}

			void GuiListControl::SetAxis(Ptr<compositions::IGuiAxis> value)
			{
				Ptr<IGuiAxis> old = axis;
				axis = value;
				SetStyleAndArranger(itemStyleProperty, itemArranger);
				AxisChanged.Execute(GetNotifyEventArguments());
			}

			bool GuiListControl::EnsureItemVisible(vint itemIndex)
			{
				if (itemIndex < 0 || itemIndex >= itemProvider->Count())
				{
					return false;
				}
				return itemArranger ? itemArranger->EnsureItemVisible(itemIndex) : false;
			}

			Size GuiListControl::GetAdoptedSize(Size expectedSize)
			{
				if (itemArranger)
				{
					Size controlSize = boundsComposition->GetBounds().GetSize();
					Size viewSize = containerComposition->GetBounds().GetSize();
					vint x = controlSize.x - viewSize.x;
					vint y = controlSize.y - viewSize.y;

					Size expectedViewSize(expectedSize.x - x, expectedSize.y - y);
					if (axis)
					{
						expectedViewSize = axis->RealSizeToVirtualSize(expectedViewSize);
					}
					Size adoptedViewSize = itemArranger->GetAdoptedSize(expectedViewSize);
					if (axis)
					{
						adoptedViewSize = axis->VirtualSizeToRealSize(adoptedViewSize);
					}
					return Size(adoptedViewSize.x + x, adoptedViewSize.y + y);
				}
				return expectedSize;
			}
			
			bool GuiListControl::GetDisplayItemBackground()
			{
				return displayItemBackground;
			}

			void GuiListControl::SetDisplayItemBackground(bool value)
			{
				if (displayItemBackground != value)
				{
					displayItemBackground = value;
					SetStyleAndArranger(itemStyleProperty, itemArranger);
				}
			}

/***********************************************************************
GuiSelectableListControl
***********************************************************************/

			void GuiSelectableListControl::NotifySelectionChanged()
			{
				SelectionChanged.Execute(GetNotifyEventArguments());
			}

			void GuiSelectableListControl::OnItemModified(vint start, vint count, vint newCount)
			{
				GuiListControl::OnItemModified(start, count, newCount);
				if(count!=newCount)
				{
					ClearSelection();
				}
			}

			void GuiSelectableListControl::OnStyleInstalled(vint itemIndex, ItemStyle* style)
			{
				GuiListControl::OnStyleInstalled(itemIndex, style);
				style->SetSelected(selectedItems.Contains(itemIndex));
			}

			void GuiSelectableListControl::OnItemSelectionChanged(vint itemIndex, bool value)
			{
				if(auto style = itemArranger->GetVisibleStyle(itemIndex))
				{
					style->SetSelected(value);
				}
			}

			void GuiSelectableListControl::OnItemSelectionCleared()
			{
				FOREACH(ItemStyle*, style, visibleStyles.Keys())
				{
					style->SetSelected(false);
				}
			}

			void GuiSelectableListControl::OnItemLeftButtonDown(compositions::GuiGraphicsComposition* sender, compositions::GuiItemMouseEventArgs& arguments)
			{
				if(GetVisuallyEnabled())
				{
					SelectItemsByClick(arguments.itemIndex, arguments.ctrl, arguments.shift, true);
				}
			}

			void GuiSelectableListControl::OnItemRightButtonDown(compositions::GuiGraphicsComposition* sender, compositions::GuiItemMouseEventArgs& arguments)
			{
				if(GetVisuallyEnabled())
				{
					SelectItemsByClick(arguments.itemIndex, arguments.ctrl, arguments.shift, false);
				}
			}

			void GuiSelectableListControl::NormalizeSelectedItemIndexStartEnd()
			{
				if(selectedItemIndexStart<0 || selectedItemIndexStart>=itemProvider->Count())
				{
					selectedItemIndexStart=0;
				}
				if(selectedItemIndexEnd<0 || selectedItemIndexEnd>=itemProvider->Count())
				{
					selectedItemIndexEnd=0;
				}
			}

			void GuiSelectableListControl::SetMultipleItemsSelectedSilently(vint start, vint end, bool selected)
			{
				if(start>end)
				{
					vint temp=start;
					start=end;
					end=temp;
				}
				vint count=itemProvider->Count();
				if(start<0) start=0;
				if(end>=count) end=count-1;
				for(vint i=start;i<=end;i++)
				{
					if(selected)
					{
						if(!selectedItems.Contains(i))
						{
							selectedItems.Add(i);
						}
					}
					else
					{
						selectedItems.Remove(i);
					}
					OnItemSelectionChanged(i, selected);
				}
			}

			void GuiSelectableListControl::OnKeyDown(compositions::GuiGraphicsComposition* sender, compositions::GuiKeyEventArgs& arguments)
			{
				if(GetVisuallyEnabled())
				{
					if(SelectItemsByKey(arguments.code, arguments.ctrl, arguments.shift))
					{
						arguments.handled=true;
					}
				}
			}

			GuiSelectableListControl::GuiSelectableListControl(theme::ThemeName themeName, IItemProvider* _itemProvider)
				:GuiListControl(themeName, _itemProvider, true)
				,multiSelect(false)
				,selectedItemIndexStart(-1)
				,selectedItemIndexEnd(-1)
			{
				SelectionChanged.SetAssociatedComposition(boundsComposition);
				ItemLeftButtonDown.AttachMethod(this, &GuiSelectableListControl::OnItemLeftButtonDown);
				ItemRightButtonDown.AttachMethod(this, &GuiSelectableListControl::OnItemRightButtonDown);
				if(focusableComposition)
				{
					focusableComposition->GetEventReceiver()->keyDown.AttachMethod(this, &GuiSelectableListControl::OnKeyDown);
				}
			}

			GuiSelectableListControl::~GuiSelectableListControl()
			{
			}

			bool GuiSelectableListControl::GetMultiSelect()
			{
				return multiSelect;
			}

			void GuiSelectableListControl::SetMultiSelect(bool value)
			{
				if (multiSelect != value)
				{
					multiSelect = value;
					ClearSelection();
				}
			}

			const collections::SortedList<vint>& GuiSelectableListControl::GetSelectedItems()
			{
				return selectedItems;
			}

			vint GuiSelectableListControl::GetSelectedItemIndex()
			{
				return selectedItems.Count() == 1 ? selectedItems[0] : -1;
			}

			WString GuiSelectableListControl::GetSelectedItemText()
			{
				vint index = GetSelectedItemIndex();
				if (index != -1)
				{
					return itemProvider->GetTextValue(index);
				}
				return L"";
			}

			bool GuiSelectableListControl::GetSelected(vint itemIndex)
			{
				return selectedItems.Contains(itemIndex);
			}

			void GuiSelectableListControl::SetSelected(vint itemIndex, bool value)
			{
				if(value)
				{
					if(!selectedItems.Contains(itemIndex))
					{
						if(!multiSelect)
						{
							selectedItems.Clear();
							OnItemSelectionCleared();
						}
						selectedItems.Add(itemIndex);
						OnItemSelectionChanged(itemIndex, value);
						NotifySelectionChanged();
					}
				}
				else
				{
					if(selectedItems.Remove(itemIndex))
					{
						OnItemSelectionChanged(itemIndex, value);
						NotifySelectionChanged();
					}
				}
			}

			bool GuiSelectableListControl::SelectItemsByClick(vint itemIndex, bool ctrl, bool shift, bool leftButton)
			{
				NormalizeSelectedItemIndexStartEnd();
				if(0<=itemIndex && itemIndex<itemProvider->Count())
				{
					if(!leftButton)
					{
						if(selectedItems.Contains(itemIndex))
						{
							return true;
						}
					}
					if(!multiSelect)
					{
						shift=false;
						ctrl=false;
					}
					if(shift)
					{
						if(!ctrl)
						{
							SetMultipleItemsSelectedSilently(selectedItemIndexStart, selectedItemIndexEnd, false);
						}
						selectedItemIndexEnd=itemIndex;
						SetMultipleItemsSelectedSilently(selectedItemIndexStart, selectedItemIndexEnd, true);
						NotifySelectionChanged();
					}
					else
					{
						if(ctrl)
						{
							vint index=selectedItems.IndexOf(itemIndex);
							if(index==-1)
							{
								selectedItems.Add(itemIndex);
							}
							else
							{
								selectedItems.RemoveAt(index);
							}
							OnItemSelectionChanged(itemIndex, index==-1);
							NotifySelectionChanged();
						}
						else
						{
							selectedItems.Clear();
							OnItemSelectionCleared();
							selectedItems.Add(itemIndex);
							OnItemSelectionChanged(itemIndex, true);
							NotifySelectionChanged();
						}
						selectedItemIndexStart=itemIndex;
						selectedItemIndexEnd=itemIndex;
					}
					return true;
				}
				return false;
			}

			bool GuiSelectableListControl::SelectItemsByKey(vint code, bool ctrl, bool shift)
			{
				if(!GetArranger()) return false;

				NormalizeSelectedItemIndexStartEnd();
				KeyDirection keyDirection=KeyDirection::Up;
				switch(code)
				{
				case VKEY_UP:
					keyDirection=KeyDirection::Up;
					break;
				case VKEY_DOWN:
					keyDirection=KeyDirection::Down;
					break;
				case VKEY_LEFT:
					keyDirection=KeyDirection::Left;
					break;
				case VKEY_RIGHT:
					keyDirection=KeyDirection::Right;
					break;
				case VKEY_HOME:
					keyDirection=KeyDirection::Home;
					break;
				case VKEY_END:
					keyDirection=KeyDirection::End;
					break;
				case VKEY_PRIOR:
					keyDirection=KeyDirection::PageUp;
					break;
				case VKEY_NEXT:
					keyDirection=KeyDirection::PageDown;
					break;
				default:
					return false;
				}

				if(GetAxis())
				{
					keyDirection=GetAxis()->RealKeyDirectionToVirtualKeyDirection(keyDirection);
				}
				vint itemIndex=GetArranger()->FindItem(selectedItemIndexEnd, keyDirection);
				if(SelectItemsByClick(itemIndex, ctrl, shift, true))
				{
					return EnsureItemVisible(itemIndex);
				}
				else
				{
					return false;
				}
			}

			void GuiSelectableListControl::ClearSelection()
			{
				if(selectedItems.Count()>0)
				{
					selectedItems.Clear();
					OnItemSelectionCleared();
					NotifySelectionChanged();
				}
			}

			namespace list
			{

/***********************************************************************
ItemProviderBase
***********************************************************************/

				void ItemProviderBase::InvokeOnItemModified(vint start, vint count, vint newCount)
				{
					for (vint i = 0; i < callbacks.Count(); i++)
					{
						callbacks[i]->OnItemModified(start, count, newCount);
					}
				}

				ItemProviderBase::ItemProviderBase()
				{
				}

				ItemProviderBase::~ItemProviderBase()
				{
					for(vint i=0;i<callbacks.Count();i++)
					{
						callbacks[i]->OnAttached(0);
					}
				}

				bool ItemProviderBase::AttachCallback(GuiListControl::IItemProviderCallback* value)
				{
					if(callbacks.Contains(value))
					{
						return false;
					}
					else
					{
						callbacks.Add(value);
						value->OnAttached(this);
						return true;
					}
				}

				bool ItemProviderBase::DetachCallback(GuiListControl::IItemProviderCallback* value)
				{
					vint index=callbacks.IndexOf(value);
					if(index==-1)
					{
						return false;
					}
					else
					{
						value->OnAttached(0);
						callbacks.Remove(value);
						return true;
					}
				}

				void ItemProviderBase::PushEditing()
				{
					editingCounter++;
				}

				bool ItemProviderBase::PopEditing()
				{
					if (editingCounter == 0)return false;
					editingCounter--;
					return true;
				}

				bool ItemProviderBase::IsEditing()
				{
					return editingCounter > 0;
				}
			}
		}
	}
}

/***********************************************************************
.\CONTROLS\TEMPLATES\GUICONTROLTEMPLATES.CPP
***********************************************************************/

namespace vl
{
	namespace presentation
	{
		namespace templates
		{
			using namespace collections;
			using namespace controls;
			using namespace compositions;
			using namespace elements;

/***********************************************************************
GuiTemplate
***********************************************************************/

			GuiTemplate_PROPERTIES(GUI_TEMPLATE_PROPERTY_IMPL)

			controls::GuiControlHost* GuiTemplate::GetControlHostForInstance()
			{
				return GetRelatedControlHost();
			}

			void GuiTemplate::OnParentLineChanged()
			{
				GuiBoundsComposition::OnParentLineChanged();
				OnControlHostForInstanceChanged();
			}

			GuiTemplate::GuiTemplate()
			{
				GuiTemplate_PROPERTIES(GUI_TEMPLATE_PROPERTY_EVENT_INIT)
			}

			GuiTemplate::~GuiTemplate()
			{
				FinalizeInstanceRecursively(this);
			}

/***********************************************************************
Item GuiListItemTemplate
***********************************************************************/

			void GuiListItemTemplate::OnInitialize()
			{
			}

			GuiListItemTemplate_PROPERTIES(GUI_TEMPLATE_PROPERTY_IMPL)

			GuiListItemTemplate::GuiListItemTemplate()
			{
				GuiListItemTemplate_PROPERTIES(GUI_TEMPLATE_PROPERTY_EVENT_INIT)
			}

			GuiListItemTemplate::~GuiListItemTemplate()
			{
				FinalizeAggregation();
			}

			void GuiListItemTemplate::BeginEditListItem()
			{
				listControl->GetItemProvider()->PushEditing();
			}

			void GuiListItemTemplate::EndEditListItem()
			{
				CHECK_ERROR(listControl->GetItemProvider()->PopEditing(), L"GuiListItemTemplate::EndEditListItem()#BeginEditListItem and EndEditListItem calls are not paired.");
			}

			void GuiListItemTemplate::Initialize(controls::GuiListControl* _listControl)
			{
				CHECK_ERROR(listControl == nullptr, L"GuiListItemTemplate::Initialize(GuiListControl*)#This function can only be called once.");
				listControl = _listControl;
				OnInitialize();
			}

/***********************************************************************
Template Declarations
***********************************************************************/

			GUI_CONTROL_TEMPLATE_DECL(GUI_TEMPLATE_CLASS_IMPL)
			GUI_ITEM_TEMPLATE_DECL(GUI_TEMPLATE_CLASS_IMPL)
		}
	}
}

/***********************************************************************
.\CONTROLS\LISTCONTROLPACKAGE\GUILISTCONTROLITEMARRANGERS.CPP
***********************************************************************/

namespace vl
{
	namespace presentation
	{
		namespace controls
		{
			using namespace collections;
			using namespace elements;
			using namespace compositions;

			namespace list
			{

/***********************************************************************
RangedItemArrangerBase
***********************************************************************/

				void RangedItemArrangerBase::InvalidateAdoptedSize()
				{
					if (listControl)
					{
						listControl->AdoptedSizeInvalidated.Execute(listControl->GetNotifyEventArguments());
					}
				}

				vint RangedItemArrangerBase::CalculateAdoptedSize(vint expectedSize, vint count, vint itemSize)
				{
					vint visibleCount = expectedSize / itemSize;
					if (count < visibleCount)
					{
						visibleCount = count;
					}
					else if (count > visibleCount)
					{
						vint deltaA = expectedSize - count * itemSize;
						vint deltaB = itemSize - deltaA;
						if (deltaB < deltaA)
						{
							visibleCount++;
						}
					}
					return visibleCount * itemSize;
				}

				RangedItemArrangerBase::ItemStyleRecord RangedItemArrangerBase::CreateStyle(vint index)
				{
					GuiSelectableButton* backgroundButton = nullptr;
					if (listControl->GetDisplayItemBackground())
					{
						backgroundButton = new GuiSelectableButton(theme::ThemeName::ListItemBackground);
						if (auto style = listControl->GetControlTemplateObject(true)->GetBackgroundTemplate())
						{
							backgroundButton->SetControlTemplate(style);
						}
						backgroundButton->SetAutoSelection(false);
					}

					auto itemStyle = callback->RequestItem(index, backgroundButton->GetBoundsComposition());
					if (backgroundButton)
					{
						itemStyle->SetAlignmentToParent(Margin(0, 0, 0, 0));
						itemStyle->SelectedChanged.AttachLambda([=](GuiGraphicsComposition* sender, GuiEventArgs& arguments)
						{
							backgroundButton->SetSelected(itemStyle->GetSelected());
						});

						backgroundButton->SetSelected(itemStyle->GetSelected());
						backgroundButton->GetContainerComposition()->AddChild(itemStyle);
					}
					return { itemStyle, backgroundButton };
				}

				void RangedItemArrangerBase::DeleteStyle(ItemStyleRecord style)
				{
					callback->ReleaseItem(style.key);
					if (style.value)
					{
						SafeDeleteControl(style.value);
					}
				}

				compositions::GuiBoundsComposition* RangedItemArrangerBase::GetStyleBounds(ItemStyleRecord style)
				{
					return style.value ? style.value->GetBoundsComposition() : style.key;
				}

				void RangedItemArrangerBase::ClearStyles()
				{
					startIndex = 0;
					if (callback)
					{
						for (vint i = 0; i < visibleStyles.Count(); i++)
						{
							DeleteStyle(visibleStyles[i]);
						}
					}
					visibleStyles.Clear();
					viewBounds = Rect(0, 0, 0, 0);
					InvalidateItemSizeCache();
					InvalidateAdoptedSize();
				}

				void RangedItemArrangerBase::OnViewChangedInternal(Rect oldBounds, Rect newBounds)
				{
					vint endIndex = startIndex + visibleStyles.Count() - 1;
					vint newStartIndex = 0;
					vint itemCount = itemProvider->Count();
					BeginPlaceItem(true, newBounds, newStartIndex);
					if (newStartIndex < 0) newStartIndex = 0;

					StyleList newVisibleStyles;
					for (vint i = newStartIndex; i < itemCount; i++)
					{
						auto style
							= startIndex <= i && i <= endIndex
							? visibleStyles[i - startIndex]
							: CreateStyle(i)
							;
						newVisibleStyles.Add(style);

						Rect bounds;
						Margin alignmentToParent;
						PlaceItem(true, i, style, newBounds, bounds, alignmentToParent);
						if (IsItemOutOfViewBounds(i, style, bounds, newBounds))
						{
							break;
						}

						bounds.x1 -= newBounds.x1;
						bounds.x2 -= newBounds.x1;
						bounds.y1 -= newBounds.y1;
						bounds.y2 -= newBounds.y1;
					}

					vint newEndIndex = newStartIndex + newVisibleStyles.Count() - 1;
					for (vint i = 0; i < visibleStyles.Count(); i++)
					{
						vint index = startIndex + i;
						if (index < newStartIndex || index > newEndIndex)
						{
							DeleteStyle(visibleStyles[i]);
						}
					}
					CopyFrom(visibleStyles, newVisibleStyles);

					if (EndPlaceItem(true, newBounds, newStartIndex))
					{
						callback->OnTotalSizeChanged();
						InvalidateAdoptedSize();
					}
					startIndex = newStartIndex;
				}

				void RangedItemArrangerBase::RearrangeItemBounds()
				{
					vint newStartIndex = startIndex;
					BeginPlaceItem(false, viewBounds, newStartIndex);
					for (vint i = 0; i < visibleStyles.Count(); i++)
					{
						auto style = visibleStyles[i];
						Rect bounds;
						Margin alignmentToParent(-1, -1, -1, -1);
						PlaceItem(false, startIndex + i, style, viewBounds, bounds, alignmentToParent);

						bounds.x1 -= viewBounds.x1;
						bounds.x2 -= viewBounds.x1;
						bounds.y1 -= viewBounds.y1;
						bounds.y2 -= viewBounds.y1;

						callback->SetStyleAlignmentToParent(GetStyleBounds(style), alignmentToParent);
						callback->SetStyleBounds(GetStyleBounds(style), bounds);
					}
					EndPlaceItem(false, viewBounds, startIndex);
				}

				RangedItemArrangerBase::RangedItemArrangerBase()
				{
				}

				RangedItemArrangerBase::~RangedItemArrangerBase()
				{
				}

				void RangedItemArrangerBase::OnAttached(GuiListControl::IItemProvider* provider)
				{
					itemProvider = provider;
					if (provider)
					{
						OnItemModified(0, 0, provider->Count());
					}
				}

				void RangedItemArrangerBase::OnItemModified(vint start, vint count, vint newCount)
				{
					if (callback && !itemProvider->IsEditing())
					{
						suppressOnViewChanged = true;
						{
							vint visibleCount = visibleStyles.Count();
							vint itemCount = itemProvider->Count();
							SortedList<ItemStyleRecord> reusedStyles;
							for (vint i = 0; i < visibleCount; i++)
							{
								vint index = startIndex + i;
								if (index >= itemCount)
								{
									break;
								}

								vint oldIndex = -1;
								if (index < start)
								{
									oldIndex = index;
								}
								else if (index >= start + newCount)
								{
									oldIndex = index - newCount + count;
								}

								if (oldIndex != -1)
								{
									if (oldIndex >= startIndex && oldIndex < startIndex + visibleCount)
									{
										auto style = visibleStyles[oldIndex - startIndex];
										reusedStyles.Add(style);
										visibleStyles.Add(style);
									}
									else
									{
										oldIndex = -1;
									}
								}
								if (oldIndex == -1)
								{
									visibleStyles.Add(CreateStyle(index));
								}
							}

							for (vint i = 0; i < visibleCount; i++)
							{
								auto style = visibleStyles[i];
								if (!reusedStyles.Contains(style))
								{
									DeleteStyle(style);
								}
							}

							visibleStyles.RemoveRange(0, visibleCount);
							for (vint i = 0; i < visibleStyles.Count(); i++)
							{
								visibleStyles[i].key->SetIndex(startIndex + i);
							}
						}
						suppressOnViewChanged = false;

						callback->OnTotalSizeChanged();
						callback->SetViewLocation(viewBounds.LeftTop());
						InvalidateAdoptedSize();
					}
				}

				void RangedItemArrangerBase::AttachListControl(GuiListControl* value)
				{
					listControl = value;
					InvalidateAdoptedSize();
				}

				void RangedItemArrangerBase::DetachListControl()
				{
					listControl = 0;
				}

				GuiListControl::IItemArrangerCallback* RangedItemArrangerBase::GetCallback()
				{
					return callback;
				}

				void RangedItemArrangerBase::SetCallback(GuiListControl::IItemArrangerCallback* value)
				{
					if (callback != value)
					{
						ClearStyles();
						callback = value;
					}
				}

				Size RangedItemArrangerBase::GetTotalSize()
				{
					if (callback)
					{
						return OnCalculateTotalSize();
					}
					else
					{
						return Size(0, 0);
					}
				}

				GuiListControl::ItemStyle* RangedItemArrangerBase::GetVisibleStyle(vint itemIndex)
				{
					if (startIndex <= itemIndex && itemIndex < startIndex + visibleStyles.Count())
					{
						return visibleStyles[itemIndex - startIndex].key;
					}
					else
					{
						return 0;
					}
				}

				vint RangedItemArrangerBase::GetVisibleIndex(GuiListControl::ItemStyle* style)
				{
					for (vint i = 0; i < visibleStyles.Count(); i++)
					{
						if (visibleStyles[i].key == style)
						{
							return i + startIndex;
						}
					}
					return -1;
				}

				void RangedItemArrangerBase::ReloadVisibleStyles()
				{
					ClearStyles();
				}

				void RangedItemArrangerBase::OnViewChanged(Rect bounds)
				{
					if (!suppressOnViewChanged)
					{
						suppressOnViewChanged = true;
						Rect oldBounds = viewBounds;
						viewBounds = bounds;
						if (callback)
						{
							OnViewChangedInternal(oldBounds, viewBounds);
							RearrangeItemBounds();
						}
						suppressOnViewChanged = false;
					}
				}

/***********************************************************************
FixedHeightItemArranger
***********************************************************************/

				vint FixedHeightItemArranger::GetWidth()
				{
					return -1;
				}

				vint FixedHeightItemArranger::GetYOffset()
				{
					return 0;
				}

				void FixedHeightItemArranger::BeginPlaceItem(bool forMoving, Rect newBounds, vint& newStartIndex)
				{
					pi_width = GetWidth();
					if (forMoving)
					{
						pim_rowHeight = rowHeight;
						newStartIndex = (newBounds.Top() - GetYOffset()) / rowHeight;
					}
				}

				void FixedHeightItemArranger::PlaceItem(bool forMoving, vint index, ItemStyleRecord style, Rect viewBounds, Rect& bounds, Margin& alignmentToParent)
				{
					vint top = GetYOffset() + index * rowHeight;
					if (pi_width == -1)
					{
						alignmentToParent = Margin(0, -1, 0, -1);
						bounds = Rect(Point(0, top), Size(0, rowHeight));
					}
					else
					{
						alignmentToParent = Margin(-1, -1, -1, -1);
						bounds = Rect(Point(0, top), Size(pi_width, rowHeight));
					}
					if (forMoving)
					{
						vint styleHeight = callback->GetStylePreferredSize(GetStyleBounds(style)).y;
						if (pim_rowHeight < styleHeight)
						{
							pim_rowHeight = styleHeight;
						}
					}
				}

				bool FixedHeightItemArranger::IsItemOutOfViewBounds(vint index, ItemStyleRecord style, Rect bounds, Rect viewBounds)
				{
					return bounds.Top() >= viewBounds.Bottom();
				}

				bool FixedHeightItemArranger::EndPlaceItem(bool forMoving, Rect newBounds, vint newStartIndex)
				{
					if (forMoving)
					{
						if (pim_rowHeight != rowHeight)
						{
							vint offset = (pim_rowHeight - rowHeight) * newStartIndex;
							rowHeight = pim_rowHeight;
							callback->SetViewLocation(Point(0, newBounds.Top() + offset));
							return true;
						}
					}
					return false;
				}

				void FixedHeightItemArranger::InvalidateItemSizeCache()
				{
					rowHeight = 1;
				}

				Size FixedHeightItemArranger::OnCalculateTotalSize()
				{
					vint width = GetWidth();
					if (width < 0) width = 0;
					return Size(width, rowHeight * itemProvider->Count() + GetYOffset());
				}

				FixedHeightItemArranger::FixedHeightItemArranger()
				{
				}

				FixedHeightItemArranger::~FixedHeightItemArranger()
				{
				}

				vint FixedHeightItemArranger::FindItem(vint itemIndex, compositions::KeyDirection key)
				{
					vint count = itemProvider->Count();
					if (count == 0) return -1;
					vint groupCount = viewBounds.Height() / rowHeight;
					if (groupCount == 0) groupCount = 1;
					switch (key)
					{
					case KeyDirection::Up:
						itemIndex--;
						break;
					case KeyDirection::Down:
						itemIndex++;
						break;
					case KeyDirection::Home:
						itemIndex = 0;
						break;
					case KeyDirection::End:
						itemIndex = count;
						break;
					case KeyDirection::PageUp:
						itemIndex -= groupCount;
						break;
					case KeyDirection::PageDown:
						itemIndex += groupCount;
						break;
					default:
						return -1;
					}

					if (itemIndex < 0) return 0;
					else if (itemIndex >= count) return count - 1;
					else return itemIndex;
				}

				bool FixedHeightItemArranger::EnsureItemVisible(vint itemIndex)
				{
					if (callback)
					{
						if (itemIndex < 0 || itemIndex >= itemProvider->Count())
						{
							return false;
						}
						while (true)
						{
							vint yOffset = GetYOffset();
							vint top = itemIndex*rowHeight;
							vint bottom = top + rowHeight + yOffset;

							if (viewBounds.Height() < rowHeight)
							{
								if (viewBounds.Top() < bottom && top < viewBounds.Bottom())
								{
									break;
								}
							}

							Point location = viewBounds.LeftTop();
							if (top < viewBounds.Top())
							{
								location.y = top;
							}
							else if (viewBounds.Bottom() < bottom)
							{
								location.y = bottom - viewBounds.Height();
							}
							else
							{
								break;
							}
							callback->SetViewLocation(location);
						}
						return true;
					}
					return false;
				}

				Size FixedHeightItemArranger::GetAdoptedSize(Size expectedSize)
				{
					if (itemProvider)
					{
						vint yOffset = GetYOffset();
						vint y = expectedSize.y - yOffset;
						vint itemCount = itemProvider->Count();
						return Size(expectedSize.x, yOffset + CalculateAdoptedSize(y, itemCount, rowHeight));
					}
					return expectedSize;
				}

/***********************************************************************
FixedSizeMultiColumnItemArranger
***********************************************************************/

				void FixedSizeMultiColumnItemArranger::BeginPlaceItem(bool forMoving, Rect newBounds, vint& newStartIndex)
				{
					if (forMoving)
					{
						pim_itemSize = itemSize;
						vint rows = newBounds.Top() / itemSize.y;
						if (rows < 0) rows = 0;
						vint cols = newBounds.Width() / itemSize.x;
						if (cols < 1) cols = 1;
						newStartIndex = rows * cols;
					}
				}

				void FixedSizeMultiColumnItemArranger::PlaceItem(bool forMoving, vint index, ItemStyleRecord style, Rect viewBounds, Rect& bounds, Margin& alignmentToParent)
				{
					vint rowItems = viewBounds.Width() / itemSize.x;
					if (rowItems < 1) rowItems = 1;

					vint row = index / rowItems;
					vint col = index % rowItems;
					bounds = Rect(Point(col * itemSize.x, row * itemSize.y), itemSize);
					if (forMoving)
					{
						Size styleSize = callback->GetStylePreferredSize(GetStyleBounds(style));
						if (pim_itemSize.x < styleSize.x) pim_itemSize.x = styleSize.x;
						if (pim_itemSize.y < styleSize.y) pim_itemSize.y = styleSize.y;
					}
				}

				bool FixedSizeMultiColumnItemArranger::IsItemOutOfViewBounds(vint index, ItemStyleRecord style, Rect bounds, Rect viewBounds)
				{
					return bounds.Top() >= viewBounds.Bottom();
				}

				bool FixedSizeMultiColumnItemArranger::EndPlaceItem(bool forMoving, Rect newBounds, vint newStartIndex)
				{
					if (forMoving)
					{
						if (pim_itemSize != itemSize)
						{
							itemSize = pim_itemSize;
							return true;
						}
					}
					return false;
				}

				void FixedSizeMultiColumnItemArranger::CalculateRange(Size itemSize, Rect bounds, vint count, vint& start, vint& end)
				{
					vint startRow = bounds.Top() / itemSize.y;
					if (startRow < 0) startRow = 0;
					vint endRow = (bounds.Bottom() - 1) / itemSize.y;
					vint cols = bounds.Width() / itemSize.x;
					if (cols < 1) cols = 1;

					start = startRow*cols;
					end = (endRow + 1)*cols - 1;
					if (end >= count) end = count - 1;
				}

				void FixedSizeMultiColumnItemArranger::InvalidateItemSizeCache()
				{
					itemSize = Size(1, 1);
				}

				Size FixedSizeMultiColumnItemArranger::OnCalculateTotalSize()
				{
					vint rowItems = viewBounds.Width() / itemSize.x;
					if (rowItems < 1) rowItems = 1;
					vint rows = itemProvider->Count() / rowItems;
					if (itemProvider->Count() % rowItems) rows++;

					return Size(itemSize.x * rowItems, itemSize.y*rows);
				}

				FixedSizeMultiColumnItemArranger::FixedSizeMultiColumnItemArranger()
				{
				}

				FixedSizeMultiColumnItemArranger::~FixedSizeMultiColumnItemArranger()
				{
				}

				vint FixedSizeMultiColumnItemArranger::FindItem(vint itemIndex, compositions::KeyDirection key)
				{
					vint count = itemProvider->Count();
					vint columnCount = viewBounds.Width() / itemSize.x;
					if (columnCount == 0) columnCount = 1;
					vint rowCount = viewBounds.Height() / itemSize.y;
					if (rowCount == 0) rowCount = 1;

					switch (key)
					{
					case KeyDirection::Up:
						itemIndex -= columnCount;
						break;
					case KeyDirection::Down:
						itemIndex += columnCount;
						break;
					case KeyDirection::Left:
						itemIndex--;
						break;
					case KeyDirection::Right:
						itemIndex++;
						break;
					case KeyDirection::Home:
						itemIndex = 0;
						break;
					case KeyDirection::End:
						itemIndex = count;
						break;
					case KeyDirection::PageUp:
						itemIndex -= columnCount*rowCount;
						break;
					case KeyDirection::PageDown:
						itemIndex += columnCount*rowCount;
						break;
					case KeyDirection::PageLeft:
						itemIndex -= itemIndex%columnCount;
						break;
					case KeyDirection::PageRight:
						itemIndex += columnCount - itemIndex%columnCount - 1;
						break;
					default:
						return -1;
					}

					if (itemIndex < 0) return 0;
					else if (itemIndex >= count) return count - 1;
					else return itemIndex;
				}

				bool FixedSizeMultiColumnItemArranger::EnsureItemVisible(vint itemIndex)
				{
					if (callback)
					{
						if (itemIndex < 0 || itemIndex >= itemProvider->Count())
						{
							return false;
						}
						while (true)
						{
							vint rowHeight = itemSize.y;
							vint columnCount = viewBounds.Width() / itemSize.x;
							if (columnCount == 0) columnCount = 1;
							vint rowIndex = itemIndex / columnCount;

							vint top = rowIndex*rowHeight;
							vint bottom = top + rowHeight;

							if (viewBounds.Height() < rowHeight)
							{
								if (viewBounds.Top() < bottom && top < viewBounds.Bottom())
								{
									break;
								}
							}

							Point location = viewBounds.LeftTop();
							if (top < viewBounds.Top())
							{
								location.y = top;
							}
							else if (viewBounds.Bottom() < bottom)
							{
								location.y = bottom - viewBounds.Height();
							}
							else
							{
								break;
							}
							callback->SetViewLocation(location);
						}
						return true;
					}
					return false;
				}

				Size FixedSizeMultiColumnItemArranger::GetAdoptedSize(Size expectedSize)
				{
					if (itemProvider)
					{
						vint count = itemProvider->Count();
						vint columnCount = viewBounds.Width() / itemSize.x;
						vint rowCount = viewBounds.Height() / itemSize.y;
						return Size(
							CalculateAdoptedSize(expectedSize.x, columnCount, itemSize.x),
							CalculateAdoptedSize(expectedSize.y, rowCount, itemSize.y)
						);
					}
					return expectedSize;
				}

/***********************************************************************
FixedHeightMultiColumnItemArranger
***********************************************************************/

				void FixedHeightMultiColumnItemArranger::CalculateRange(vint itemHeight, Rect bounds, vint& rows, vint& startColumn)
				{
					rows = bounds.Height() / itemHeight;
					if (rows < 1) rows = 1;
					startColumn = bounds.Left() / bounds.Width();
				}

				void FixedHeightMultiColumnItemArranger::BeginPlaceItem(bool forMoving, Rect newBounds, vint& newStartIndex)
				{
					pi_currentWidth = 0;
					pi_totalWidth = 0;
					if (forMoving)
					{
						pim_itemHeight = itemHeight;
						vint rows = newBounds.Height() / itemHeight;
						if (rows < 1) rows = 1;
						vint columns = newBounds.Left() / newBounds.Width();
						newStartIndex = rows * columns;
					}
				}

				void FixedHeightMultiColumnItemArranger::PlaceItem(bool forMoving, vint index, ItemStyleRecord style, Rect viewBounds, Rect& bounds, Margin& alignmentToParent)
				{
					vint rows = viewBounds.Height() / itemHeight;
					if (rows < 1) rows = 1;

					vint row = index % rows;
					if (row == 0)
					{
						pi_totalWidth += pi_currentWidth;
						pi_currentWidth = 0;
					}

					Size styleSize = callback->GetStylePreferredSize(GetStyleBounds(style));
					if (pi_currentWidth < styleSize.x) pi_currentWidth = styleSize.x;
					bounds = Rect(Point(pi_totalWidth + viewBounds.Left(), itemHeight * row), Size(0, 0));
					if (forMoving)
					{
						if (pim_itemHeight < styleSize.y) pim_itemHeight = styleSize.y;
					}
				}

				bool FixedHeightMultiColumnItemArranger::IsItemOutOfViewBounds(vint index, ItemStyleRecord style, Rect bounds, Rect viewBounds)
				{
					return bounds.Left() >= viewBounds.Right();
				}

				bool FixedHeightMultiColumnItemArranger::EndPlaceItem(bool forMoving, Rect newBounds, vint newStartIndex)
				{
					if (forMoving)
					{
						if (pim_itemHeight != itemHeight)
						{
							itemHeight = pim_itemHeight;
							return true;
						}
					}
					return false;
				}

				void FixedHeightMultiColumnItemArranger::InvalidateItemSizeCache()
				{
					itemHeight = 1;
				}

				Size FixedHeightMultiColumnItemArranger::OnCalculateTotalSize()
				{
					vint rows = viewBounds.Height() / itemHeight;
					if (rows < 1) rows = 1;
					vint columns = itemProvider->Count() / rows;
					if (itemProvider->Count() % rows) columns += 1;
					return Size(viewBounds.Width() * columns, 0);
				}

				FixedHeightMultiColumnItemArranger::FixedHeightMultiColumnItemArranger()
					:itemHeight(1)
				{
				}

				FixedHeightMultiColumnItemArranger::~FixedHeightMultiColumnItemArranger()
				{
				}

				vint FixedHeightMultiColumnItemArranger::FindItem(vint itemIndex, compositions::KeyDirection key)
				{
					vint count = itemProvider->Count();
					vint groupCount = viewBounds.Height() / itemHeight;
					if (groupCount == 0) groupCount = 1;
					switch (key)
					{
					case KeyDirection::Up:
						itemIndex--;
						break;
					case KeyDirection::Down:
						itemIndex++;
						break;
					case KeyDirection::Left:
						itemIndex -= groupCount;
						break;
					case KeyDirection::Right:
						itemIndex += groupCount;
						break;
					case KeyDirection::Home:
						itemIndex = 0;
						break;
					case KeyDirection::End:
						itemIndex = count;
						break;
					case KeyDirection::PageUp:
						itemIndex -= itemIndex%groupCount;
						break;
					case KeyDirection::PageDown:
						itemIndex += groupCount - itemIndex%groupCount - 1;
						break;
					default:
						return -1;
					}

					if (itemIndex < 0) return 0;
					else if (itemIndex >= count) return count - 1;
					else return itemIndex;
				}

				bool FixedHeightMultiColumnItemArranger::EnsureItemVisible(vint itemIndex)
				{
					if (callback)
					{
						if (itemIndex < 0 || itemIndex >= itemProvider->Count())
						{
							return false;
						}
						while (true)
						{
							vint rowCount = viewBounds.Height() / itemHeight;
							if (rowCount == 0) rowCount = 1;
							vint columnIndex = itemIndex / rowCount;
							vint minIndex = startIndex;
							vint maxIndex = startIndex + visibleStyles.Count() - 1;

							Point location = viewBounds.LeftTop();
							if (minIndex <= itemIndex && itemIndex <= maxIndex)
							{
								Rect bounds = callback->GetStyleBounds(GetStyleBounds(visibleStyles[itemIndex - startIndex]));
								if (0 < bounds.Bottom() && bounds.Top() < viewBounds.Width() && bounds.Width() > viewBounds.Width())
								{
									break;
								}
								else if (bounds.Left() < 0)
								{
									location.x -= viewBounds.Width();
								}
								else if (bounds.Right() > viewBounds.Width())
								{
									location.x += viewBounds.Width();
								}
								else
								{
									break;
								}
							}
							else if (columnIndex < minIndex / rowCount)
							{
								location.x -= viewBounds.Width();
							}
							else if (columnIndex >= maxIndex / rowCount)
							{
								location.x += viewBounds.Width();
							}
							else
							{
								break;
							}
							callback->SetViewLocation(location);
						}
						return true;
					}
					return false;
				}

				Size FixedHeightMultiColumnItemArranger::GetAdoptedSize(Size expectedSize)
				{
					if (itemProvider)
					{
						vint count = itemProvider->Count();
						return Size(expectedSize.x, CalculateAdoptedSize(expectedSize.y, count, itemHeight));
					}
					return expectedSize;
				}
			}
		}
	}
}

/***********************************************************************
.\CONTROLS\LISTCONTROLPACKAGE\GUITEXTLISTCONTROLS.CPP
***********************************************************************/

namespace vl
{
	namespace presentation
	{
		namespace controls
		{
			using namespace collections;
			using namespace elements;
			using namespace compositions;
			using namespace reflection::description;

			namespace list
			{
				const wchar_t* const ITextItemView::Identifier = L"vl::presentation::controls::list::ITextItemView";

/***********************************************************************
DefaultTextListItemTemplate
***********************************************************************/

				TemplateProperty<DefaultTextListItemTemplate::BulletStyle> DefaultTextListItemTemplate::CreateBulletStyle()
				{
					return {};
				}

				void DefaultTextListItemTemplate::OnInitialize()
				{
					SetMinSizeLimitation(GuiGraphicsComposition::LimitToElementAndChildren);

					textElement = GuiSolidLabelElement::Create();
					textElement->SetAlignments(Alignment::Left, Alignment::Center);

					GuiBoundsComposition* textComposition = new GuiBoundsComposition;
					textComposition->SetOwnedElement(textElement);
					textComposition->SetMinSizeLimitation(GuiGraphicsComposition::LimitToElement);

					if (auto bulletStyleController = CreateBulletStyle())
					{
						bulletButton = new GuiSelectableButton(theme::ThemeName::Unknown);
						bulletButton->SetControlTemplate(bulletStyleController);
						bulletButton->GetBoundsComposition()->SetAlignmentToParent(Margin(0, 0, 0, 0));
						bulletButton->SelectedChanged.AttachMethod(this, &DefaultTextListItemTemplate::OnBulletSelectedChanged);

						GuiTableComposition* table = new GuiTableComposition;
						AddChild(table);
						table->SetAlignmentToParent(Margin(0, 0, 0, 0));
						table->SetMinSizeLimitation(GuiGraphicsComposition::LimitToElementAndChildren);
						table->SetRowsAndColumns(1, 2);
						table->SetRowOption(0, GuiCellOption::PercentageOption(1.0));
						table->SetColumnOption(0, GuiCellOption::MinSizeOption());
						table->SetColumnOption(1, GuiCellOption::PercentageOption(1.0));
						{
							GuiCellComposition* cell = new GuiCellComposition;
							table->AddChild(cell);
							cell->SetSite(0, 0, 1, 1);
							cell->AddChild(bulletButton->GetBoundsComposition());
						}
						{
							GuiCellComposition* cell = new GuiCellComposition;
							table->AddChild(cell);
							cell->SetSite(0, 1, 1, 1);
							cell->AddChild(textComposition);
							textComposition->SetAlignmentToParent(Margin(0, 0, 0, 0));
						}
					}
					else
					{
						AddChild(textComposition);
						textComposition->SetAlignmentToParent(Margin(5, 0, 0, 0));
					}

					FontChanged.AttachMethod(this, &DefaultTextListItemTemplate::OnFontChanged);
					TextChanged.AttachMethod(this, &DefaultTextListItemTemplate::OnTextChanged);
					TextColorChanged.AttachMethod(this, &DefaultTextListItemTemplate::OnTextColorChanged);
					CheckedChanged.AttachMethod(this, &DefaultTextListItemTemplate::OnCheckedChanged);

					FontChanged.Execute(compositions::GuiEventArgs(this));
					TextChanged.Execute(compositions::GuiEventArgs(this));
					TextColorChanged.Execute(compositions::GuiEventArgs(this));
					CheckedChanged.Execute(compositions::GuiEventArgs(this));
				}

				void DefaultTextListItemTemplate::OnFontChanged(compositions::GuiGraphicsComposition* sender, compositions::GuiEventArgs& arguments)
				{
					textElement->SetFont(GetFont());
				}

				void DefaultTextListItemTemplate::OnTextChanged(compositions::GuiGraphicsComposition* sender, compositions::GuiEventArgs& arguments)
				{
					textElement->SetText(GetText());
				}

				void DefaultTextListItemTemplate::OnTextColorChanged(compositions::GuiGraphicsComposition* sender, compositions::GuiEventArgs& arguments)
				{
					textElement->SetColor(GetTextColor());
				}

				void DefaultTextListItemTemplate::OnCheckedChanged(compositions::GuiGraphicsComposition* sender, compositions::GuiEventArgs& arguments)
				{
					if (bulletButton)
					{
						supressEdit = true;
						bulletButton->SetSelected(GetChecked());
						supressEdit = false;
					}
				}

				void DefaultTextListItemTemplate::OnBulletSelectedChanged(compositions::GuiGraphicsComposition* sender, compositions::GuiEventArgs& arguments)
				{
					if (!supressEdit)
					{
						if (auto textItemView = dynamic_cast<ITextItemView*>(listControl->GetItemProvider()->RequestView(ITextItemView::Identifier)))
						{
							BeginEditListItem();
							textItemView->SetChecked(GetIndex(), bulletButton->GetSelected());
							EndEditListItem();
						}
					}
				}

				DefaultTextListItemTemplate::DefaultTextListItemTemplate()
				{
				}

				DefaultTextListItemTemplate::~DefaultTextListItemTemplate()
				{
				}

/***********************************************************************
DefaultCheckTextListItemTemplate
***********************************************************************/

				TemplateProperty<DefaultTextListItemTemplate::BulletStyle> DefaultCheckTextListItemTemplate::CreateBulletStyle()
				{
					if (auto textList = dynamic_cast<GuiVirtualTextList*>(listControl))
					{
						auto style = textList->GetControlTemplateObject(true)->GetCheckBulletTemplate();
						if (style) return style;
					}
					return theme::GetCurrentTheme()->CreateStyle(theme::ThemeName::CheckTextListItem);
				}

/***********************************************************************
DefaultRadioTextListItemTemplate
***********************************************************************/

				TemplateProperty<DefaultTextListItemTemplate::BulletStyle> DefaultRadioTextListItemTemplate::CreateBulletStyle()
				{
					if (auto textList = dynamic_cast<GuiVirtualTextList*>(listControl))
					{
						auto style = textList->GetControlTemplateObject(true)->GetRadioBulletTemplate();
						if (style) return style;
					}
					return theme::GetCurrentTheme()->CreateStyle(theme::ThemeName::RadioTextListItem);
				}

/***********************************************************************
TextItem
***********************************************************************/

				TextItem::TextItem()
					:owner(0)
					, checked(false)
				{
				}

				TextItem::TextItem(const WString& _text, bool _checked)
					:owner(0)
					, text(_text)
					, checked(_checked)
				{
				}

				TextItem::~TextItem()
				{
				}

				bool TextItem::operator==(const TextItem& value)const
				{
					return text==value.text;
				}

				bool TextItem::operator!=(const TextItem& value)const
				{
					return text!=value.text;
				}

				const WString& TextItem::GetText()
				{
					return text;
				}

				void TextItem::SetText(const WString& value)
				{
					if (text != value)
					{
						text = value;
						if (owner)
						{
							vint index = owner->IndexOf(this);
							owner->InvokeOnItemModified(index, 1, 1);
						}
					}
				}

				bool TextItem::GetChecked()
				{
					return checked;
				}

				void TextItem::SetChecked(bool value)
				{
					if (checked != value)
					{
						checked = value;
						if (owner)
						{
							vint index = owner->IndexOf(this);
							owner->InvokeOnItemModified(index, 1, 1);

							GuiItemEventArgs arguments;
							arguments.itemIndex = index;
							owner->listControl->ItemChecked.Execute(arguments);
						}
					}
				}

/***********************************************************************
TextItemProvider
***********************************************************************/

				void TextItemProvider::AfterInsert(vint item, const Ptr<TextItem>& value)
				{
					ListProvider<Ptr<TextItem>>::AfterInsert(item, value);
					value->owner = this;
				}

				void TextItemProvider::BeforeRemove(vint item, const Ptr<TextItem>& value)
				{
					value->owner = 0;
					ListProvider<Ptr<TextItem>>::BeforeRemove(item, value);
				}

				WString TextItemProvider::GetTextValue(vint itemIndex)
				{
					return Get(itemIndex)->GetText();
				}

				description::Value TextItemProvider::GetBindingValue(vint itemIndex)
				{
					return Value::From(Get(itemIndex));
				}

				bool TextItemProvider::GetChecked(vint itemIndex)
				{
					return Get(itemIndex)->GetChecked();
				}

				void TextItemProvider::SetChecked(vint itemIndex, bool value)
				{
					return Get(itemIndex)->SetChecked(value);
				}

				TextItemProvider::TextItemProvider()
					:listControl(0)
				{
				}

				TextItemProvider::~TextItemProvider()
				{
				}

				IDescriptable* TextItemProvider::RequestView(const WString& identifier)
				{
					if (identifier == ITextItemView::Identifier)
					{
						return (ITextItemView*)this;
					}
					else
					{
						return nullptr;
					}
				}
			}

/***********************************************************************
GuiTextList
***********************************************************************/

			void GuiVirtualTextList::BeforeControlTemplateUninstalled_()
			{
			}

			void GuiVirtualTextList::AfterControlTemplateInstalled_(bool initialize)
			{
			}

			void GuiVirtualTextList::OnStyleInstalled(vint itemIndex, ItemStyle* style)
			{
				GuiSelectableListControl::OnStyleInstalled(itemIndex, style);
				if (auto textItemStyle = dynamic_cast<templates::GuiTextListItemTemplate*>(style))
				{
					textItemStyle->SetTextColor(GetControlTemplateObject(true)->GetTextColor());
					if (auto textItemView = dynamic_cast<list::ITextItemView*>(itemProvider->RequestView(list::ITextItemView::Identifier)))
					{
						textItemStyle->SetChecked(textItemView->GetChecked(itemIndex));
					}
				}
			}

			void GuiVirtualTextList::OnItemTemplateChanged(compositions::GuiGraphicsComposition* sender, compositions::GuiEventArgs& arguments)
			{
				view = TextListView::Unknown;
			}

			GuiVirtualTextList::GuiVirtualTextList(theme::ThemeName themeName, GuiListControl::IItemProvider* _itemProvider)
				:GuiSelectableListControl(themeName, _itemProvider)
			{
				ItemTemplateChanged.AttachMethod(this, &GuiVirtualTextList::OnItemTemplateChanged);
				ItemChecked.SetAssociatedComposition(boundsComposition);

				SetView(TextListView::Text);
			}

			GuiVirtualTextList::~GuiVirtualTextList()
			{
			}

			TextListView GuiVirtualTextList::GetView()
			{
				return view;
			}

			void GuiVirtualTextList::SetView(TextListView _view)
			{
				switch (_view)
				{
				case TextListView::Text:
					SetStyleAndArranger(
						[](const Value&) { return new list::DefaultTextListItemTemplate; },
						new list::FixedHeightItemArranger
					);
					break;
				case TextListView::Check:
					SetStyleAndArranger(
						[](const Value&) { return new list::DefaultCheckTextListItemTemplate; },
						new list::FixedHeightItemArranger
					);
					break;
				case TextListView::Radio:
					SetStyleAndArranger(
						[](const Value&) { return new list::DefaultRadioTextListItemTemplate; },
						new list::FixedHeightItemArranger
					);
					break;
				default:;
				}
				view = _view;
			}

/***********************************************************************
GuiTextList
***********************************************************************/

			GuiTextList::GuiTextList(theme::ThemeName themeName)
				:GuiVirtualTextList(themeName, new list::TextItemProvider)
			{
				items=dynamic_cast<list::TextItemProvider*>(itemProvider.Obj());
				items->listControl=this;
			}

			GuiTextList::~GuiTextList()
			{
			}

			list::TextItemProvider& GuiTextList::GetItems()
			{
				return *items;
			}

			Ptr<list::TextItem> GuiTextList::GetSelectedItem()
			{
				vint index = GetSelectedItemIndex();
				if (index == -1) return 0;
				return items->Get(index);
			}
		}
	}
}

/***********************************************************************
.\CONTROLS\LISTCONTROLPACKAGE\GUITREEVIEWCONTROLS.CPP
***********************************************************************/

namespace vl
{
	namespace presentation
	{
		namespace controls
		{
			using namespace elements;
			using namespace compositions;
			using namespace reflection::description;

			namespace tree
			{
				const wchar_t* const INodeItemView::Identifier = L"vl::presentation::controls::tree::INodeItemView";

/***********************************************************************
NodeItemProvider
***********************************************************************/

				Ptr<INodeProvider> NodeItemProvider::GetNodeByOffset(Ptr<INodeProvider> provider, vint offset)
				{
					if (offset == 0) return provider;
					Ptr<INodeProvider> result;
					if (provider->GetExpanding() && offset > 0)
					{
						offset -= 1;
						vint count = provider->GetChildCount();
						for (vint i = 0; (!result && i < count); i++)
						{
							auto child = provider->GetChild(i);
							vint visibleCount = child->CalculateTotalVisibleNodes();
							if (offset < visibleCount)
							{
								result = GetNodeByOffset(child, offset);
							}
							else
							{
								offset -= visibleCount;
							}
						}
					}
					return result;
				}

				void NodeItemProvider::OnAttached(INodeRootProvider* provider)
				{
				}

				void NodeItemProvider::OnBeforeItemModified(INodeProvider* parentNode, vint start, vint count, vint newCount)
				{
					vint offset = 0;
					vint base = CalculateNodeVisibilityIndexInternal(parentNode);
					if (base != -2 && parentNode->GetExpanding())
					{
						for (vint i = 0; i < count; i++)
						{
							auto child = parentNode->GetChild(start + i);
							offset += child->CalculateTotalVisibleNodes();
						}
					}
					offsetBeforeChildModifieds.Set(parentNode, offset);
				}

				void NodeItemProvider::OnAfterItemModified(INodeProvider* parentNode, vint start, vint count, vint newCount)
				{
					vint offsetBeforeChildModified = 0;
					{
						vint index = offsetBeforeChildModifieds.Keys().IndexOf(parentNode);
						if (index != -1)
						{
							offsetBeforeChildModified = offsetBeforeChildModifieds.Values().Get(index);
							offsetBeforeChildModifieds.Remove(parentNode);
						}
					}

					vint base = CalculateNodeVisibilityIndexInternal(parentNode);
					if (base != -2 && parentNode->GetExpanding())
					{
						vint offset = 0;
						vint firstChildStart = -1;
						for (vint i = 0; i < newCount; i++)
						{
							auto child = parentNode->GetChild(start + i);
							if (i == 0)
							{
								firstChildStart = CalculateNodeVisibilityIndexInternal(child.Obj());
							}
							offset += child->CalculateTotalVisibleNodes();
						}

						if (firstChildStart == -1)
						{
							vint childCount = parentNode->GetChildCount();
							if (childCount == 0)
							{
								firstChildStart = base + 1;
							}
							else if (start < childCount)
							{
								auto child = parentNode->GetChild(start);
								firstChildStart = CalculateNodeVisibilityIndexInternal(child.Obj());
							}
							else
							{
								auto child = parentNode->GetChild(start - 1);
								firstChildStart = CalculateNodeVisibilityIndexInternal(child.Obj());
								firstChildStart += child->CalculateTotalVisibleNodes();
							}
						}
						InvokeOnItemModified(firstChildStart, offsetBeforeChildModified, offset);
					}
				}

				void NodeItemProvider::OnItemExpanded(INodeProvider* node)
				{
					vint base = CalculateNodeVisibilityIndexInternal(node);
					if (base != -2)
					{
						vint visibility = node->CalculateTotalVisibleNodes();
						InvokeOnItemModified(base + 1, 0, visibility - 1);
					}
				}

				void NodeItemProvider::OnItemCollapsed(INodeProvider* node)
				{
					vint base = CalculateNodeVisibilityIndexInternal(node);
					if (base != -2)
					{
						vint visibility = 0;
						vint count = node->GetChildCount();
						for (vint i = 0; i < count; i++)
						{
							auto child = node->GetChild(i);
							visibility += child->CalculateTotalVisibleNodes();
						}
						InvokeOnItemModified(base + 1, visibility, 0);
					}
				}

				vint NodeItemProvider::CalculateNodeVisibilityIndexInternal(INodeProvider* node)
				{
					auto parent = node->GetParent();
					if (!parent)
					{
						return -1;
					}
					if (!parent->GetExpanding())
					{
						return -2;
					}

					vint index = CalculateNodeVisibilityIndexInternal(parent.Obj());
					if (index == -2)
					{
						return -2;
					}

					vint count = parent->GetChildCount();
					for (vint i = 0; i < count; i++)
					{
						auto child = parent->GetChild(i);
						bool findResult = child == node;
						if (findResult)
						{
							index++;
						}
						else
						{
							index += child->CalculateTotalVisibleNodes();
						}
						if (findResult)
						{
							return index;
						}
					}
					return -1;
				}

				vint NodeItemProvider::CalculateNodeVisibilityIndex(INodeProvider* node)
				{
					vint result = CalculateNodeVisibilityIndexInternal(node);
					return result < 0 ? -1 : result;
				}

				Ptr<INodeProvider> NodeItemProvider::RequestNode(vint index)
				{
					if(root->CanGetNodeByVisibleIndex())
					{
						return root->GetNodeByVisibleIndex(index+1);
					}
					else
					{
						return GetNodeByOffset(root->GetRootNode(), index+1);
					}
				}

				NodeItemProvider::NodeItemProvider(Ptr<INodeRootProvider> _root)
					:root(_root)
				{
					root->AttachCallback(this);
				}

				NodeItemProvider::~NodeItemProvider()
				{
					root->DetachCallback(this);
				}

				Ptr<INodeRootProvider> NodeItemProvider::GetRoot()
				{
					return root;
				}

				vint NodeItemProvider::Count()
				{
					return root->GetRootNode()->CalculateTotalVisibleNodes()-1;
				}

				WString NodeItemProvider::GetTextValue(vint itemIndex)
				{
					if (auto node = RequestNode(itemIndex))
					{
						return root->GetTextValue(node.Obj());
					}
					return L"";
				}

				description::Value NodeItemProvider::GetBindingValue(vint itemIndex)
				{
					if (auto node = RequestNode(itemIndex))
					{
						return root->GetBindingValue(node.Obj());
					}
					return Value();
				}

				IDescriptable* NodeItemProvider::RequestView(const WString& identifier)
				{
					if(identifier==INodeItemView::Identifier)
					{
						return (INodeItemView*)this;
					}
					else
					{
						return root->RequestView(identifier);
					}
				}

/***********************************************************************
MemoryNodeProvider::NodeCollection
***********************************************************************/

				void MemoryNodeProvider::NodeCollection::OnBeforeChildModified(vint start, vint count, vint newCount)
				{
					if (ownerProvider->expanding)
					{
						for (vint i = 0; i < count; i++)
						{
							offsetBeforeChildModified += items[start + i]->totalVisibleNodeCount;
						}
					}
					INodeProviderCallback* proxy = ownerProvider->GetCallbackProxyInternal();
					if (proxy)
					{
						proxy->OnBeforeItemModified(ownerProvider, start, count, newCount);
					}
				}

				void MemoryNodeProvider::NodeCollection::OnAfterChildModified(vint start, vint count, vint newCount)
				{
					ownerProvider->childCount += (newCount - count);
					if (ownerProvider->expanding)
					{
						vint offset = 0;
						for (vint i = 0; i < newCount; i++)
						{
							offset += items[start + i]->totalVisibleNodeCount;
						}
						ownerProvider->OnChildTotalVisibleNodesChanged(offset - offsetBeforeChildModified);
					}
					offsetBeforeChildModified = 0;
					INodeProviderCallback* proxy = ownerProvider->GetCallbackProxyInternal();
					if (proxy)
					{
						proxy->OnAfterItemModified(ownerProvider, start, count, newCount);
					}
				}

				bool MemoryNodeProvider::NodeCollection::QueryInsert(vint index, Ptr<MemoryNodeProvider> const& child)
				{
					return child->parent == 0;
				}

				bool MemoryNodeProvider::NodeCollection::QueryRemove(vint index, Ptr<MemoryNodeProvider> const& child)
				{
					return child->parent == ownerProvider;
				}

				void MemoryNodeProvider::NodeCollection::BeforeInsert(vint index, Ptr<MemoryNodeProvider> const& child)
				{
					OnBeforeChildModified(index, 0, 1);
					child->parent = ownerProvider;
				}

				void MemoryNodeProvider::NodeCollection::BeforeRemove(vint index, Ptr<MemoryNodeProvider> const& child)
				{
					OnBeforeChildModified(index, 1, 0);
					child->parent = 0;
				}

				void MemoryNodeProvider::NodeCollection::AfterInsert(vint index, Ptr<MemoryNodeProvider> const& child)
				{
					OnAfterChildModified(index, 0, 1);
				}

				void MemoryNodeProvider::NodeCollection::AfterRemove(vint index, vint count)
				{
					OnAfterChildModified(index, count, 0);
				}

				MemoryNodeProvider::NodeCollection::NodeCollection()
					:ownerProvider(0)
				{
				}

/***********************************************************************
MemoryNodeProvider
***********************************************************************/

				INodeProviderCallback* MemoryNodeProvider::GetCallbackProxyInternal()
				{
					if(parent)
					{
						return parent->GetCallbackProxyInternal();
					}
					else
					{
						return 0;
					}
				}

				void MemoryNodeProvider::OnChildTotalVisibleNodesChanged(vint offset)
				{
					totalVisibleNodeCount+=offset;
					if(parent)
					{
						parent->OnChildTotalVisibleNodesChanged(offset);
					}
				}

				MemoryNodeProvider::MemoryNodeProvider(Ptr<DescriptableObject> _data)
					:data(_data)
				{
					children.ownerProvider=this;
				}

				MemoryNodeProvider::~MemoryNodeProvider()
				{
				}

				Ptr<DescriptableObject> MemoryNodeProvider::GetData()
				{
					return data;
				}

				void MemoryNodeProvider::SetData(const Ptr<DescriptableObject>& value)
				{
					data=value;
					NotifyDataModified();
				}

				void MemoryNodeProvider::NotifyDataModified()
				{
					if(parent)
					{
						vint index=parent->children.IndexOf(this);
						INodeProviderCallback* proxy=GetCallbackProxyInternal();
						if(proxy)
						{
							proxy->OnBeforeItemModified(parent, index, 1, 1);
							proxy->OnAfterItemModified(parent, index, 1, 1);
						}
					}
				}

				MemoryNodeProvider::NodeCollection& MemoryNodeProvider::Children()
				{
					return children;
				}

				bool MemoryNodeProvider::GetExpanding()
				{
					return expanding;
				}

				void MemoryNodeProvider::SetExpanding(bool value)
				{
					if(expanding!=value)
					{
						expanding=value;
						vint offset=0;
						for(vint i=0;i<childCount;i++)
						{
							offset+=children[i]->totalVisibleNodeCount;
						}

						OnChildTotalVisibleNodesChanged(expanding?offset:-offset);
						INodeProviderCallback* proxy=GetCallbackProxyInternal();
						if(proxy)
						{
							if(expanding)
							{
								proxy->OnItemExpanded(this);
							}
							else
							{
								proxy->OnItemCollapsed(this);
							}
						}
					}
				}

				vint MemoryNodeProvider::CalculateTotalVisibleNodes()
				{
					return totalVisibleNodeCount;
				}

				vint MemoryNodeProvider::GetChildCount()
				{
					return childCount;
				}

				Ptr<INodeProvider> MemoryNodeProvider::GetParent()
				{
					return parent;
				}

				Ptr<INodeProvider> MemoryNodeProvider::GetChild(vint index)
				{
					if(0<=index && index<childCount)
					{
						return children[index].Obj();
					}
					else
					{
						return nullptr;
					}
				}

/***********************************************************************
NodeRootProviderBase
***********************************************************************/

				void NodeRootProviderBase::OnAttached(INodeRootProvider* provider)
				{
				}

				void NodeRootProviderBase::OnBeforeItemModified(INodeProvider* parentNode, vint start, vint count, vint newCount)
				{
					for(vint i=0;i<callbacks.Count();i++)
					{
						callbacks[i]->OnBeforeItemModified(parentNode, start, count, newCount);
					}
				}

				void NodeRootProviderBase::OnAfterItemModified(INodeProvider* parentNode, vint start, vint count, vint newCount)
				{
					for(vint i=0;i<callbacks.Count();i++)
					{
						callbacks[i]->OnAfterItemModified(parentNode, start, count, newCount);
					}
				}

				void NodeRootProviderBase::OnItemExpanded(INodeProvider* node)
				{
					for(vint i=0;i<callbacks.Count();i++)
					{
						callbacks[i]->OnItemExpanded(node);
					}
				}

				void NodeRootProviderBase::OnItemCollapsed(INodeProvider* node)
				{
					for(vint i=0;i<callbacks.Count();i++)
					{
						callbacks[i]->OnItemCollapsed(node);
					}
				}

				NodeRootProviderBase::NodeRootProviderBase()
				{
				}

				NodeRootProviderBase::~NodeRootProviderBase()
				{
				}

				bool NodeRootProviderBase::CanGetNodeByVisibleIndex()
				{
					return false;
				}

				Ptr<INodeProvider> NodeRootProviderBase::GetNodeByVisibleIndex(vint index)
				{
					return nullptr;
				}

				bool NodeRootProviderBase::AttachCallback(INodeProviderCallback* value)
				{
					if(callbacks.Contains(value))
					{
						return false;
					}
					else
					{
						callbacks.Add(value);
						value->OnAttached(this);
						return true;
					}
				}

				bool NodeRootProviderBase::DetachCallback(INodeProviderCallback* value)
				{
					vint index=callbacks.IndexOf(value);
					if(index==-1)
					{
						return false;
					}
					else
					{
						value->OnAttached(0);
						callbacks.Remove(value);
						return true;
					}
				}

				IDescriptable* NodeRootProviderBase::RequestView(const WString& identifier)
				{
					return 0;
				}

/***********************************************************************
MemoryNodeRootProvider
***********************************************************************/

				INodeProviderCallback* MemoryNodeRootProvider::GetCallbackProxyInternal()
				{
					return this;
				}

				MemoryNodeRootProvider::MemoryNodeRootProvider()
				{
					SetExpanding(true);
				}

				MemoryNodeRootProvider::~MemoryNodeRootProvider()
				{
				}

				Ptr<INodeProvider> MemoryNodeRootProvider::GetRootNode()
				{
					return this;
				}

				MemoryNodeProvider* MemoryNodeRootProvider::GetMemoryNode(INodeProvider* node)
				{
					return dynamic_cast<MemoryNodeProvider*>(node);
				}
			}

/***********************************************************************
GuiVirtualTreeListControl
***********************************************************************/

			void GuiVirtualTreeListControl::BeforeControlTemplateUninstalled_()
			{
			}

			void GuiVirtualTreeListControl::AfterControlTemplateInstalled_(bool initialize)
			{
			}

			void GuiVirtualTreeListControl::OnAttached(tree::INodeRootProvider* provider)
			{
			}

			void GuiVirtualTreeListControl::OnBeforeItemModified(tree::INodeProvider* parentNode, vint start, vint count, vint newCount)
			{
			}

			void GuiVirtualTreeListControl::OnAfterItemModified(tree::INodeProvider* parentNode, vint start, vint count, vint newCount)
			{
			}

			void GuiVirtualTreeListControl::OnItemExpanded(tree::INodeProvider* node)
			{
				GuiNodeEventArgs arguments;
				(GuiEventArgs&)arguments=GetNotifyEventArguments();
				arguments.node=node;
				NodeExpanded.Execute(arguments);
			}

			void GuiVirtualTreeListControl::OnItemCollapsed(tree::INodeProvider* node)
			{
				GuiNodeEventArgs arguments;
				(GuiEventArgs&)arguments=GetNotifyEventArguments();
				arguments.node=node;
				NodeCollapsed.Execute(arguments);
			}

			void GuiVirtualTreeListControl::OnItemMouseEvent(compositions::GuiNodeMouseEvent& nodeEvent, compositions::GuiGraphicsComposition* sender, compositions::GuiItemMouseEventArgs& arguments)
			{
				auto node = GetNodeItemView()->RequestNode(arguments.itemIndex);
				if (node)
				{
					GuiNodeMouseEventArgs redirectArguments;
					(GuiMouseEventArgs&)redirectArguments = arguments;
					redirectArguments.node = node.Obj();
					nodeEvent.Execute(redirectArguments);
					(GuiMouseEventArgs&)arguments = redirectArguments;
				}
			}

			void GuiVirtualTreeListControl::OnItemNotifyEvent(compositions::GuiNodeNotifyEvent& nodeEvent, compositions::GuiGraphicsComposition* sender, compositions::GuiItemEventArgs& arguments)
			{
				if (auto node = GetNodeItemView()->RequestNode(arguments.itemIndex))
				{
					GuiNodeEventArgs redirectArguments;
					(GuiEventArgs&)redirectArguments = arguments;
					redirectArguments.node = node.Obj();
					nodeEvent.Execute(redirectArguments);
					(GuiEventArgs&)arguments = redirectArguments;
				}
			}

#define ATTACH_ITEM_MOUSE_EVENT(NODEEVENTNAME, ITEMEVENTNAME)\
					{\
						Func<void(GuiNodeMouseEvent&, GuiGraphicsComposition*, GuiItemMouseEventArgs&)> func(this, &GuiVirtualTreeListControl::OnItemMouseEvent);\
						ITEMEVENTNAME.AttachFunction(Curry(func)(NODEEVENTNAME));\
					}\

#define ATTACH_ITEM_NOTIFY_EVENT(NODEEVENTNAME, ITEMEVENTNAME)\
					{\
						Func<void(GuiNodeNotifyEvent&, GuiGraphicsComposition*, GuiItemEventArgs&)> func(this, &GuiVirtualTreeListControl::OnItemNotifyEvent);\
						ITEMEVENTNAME.AttachFunction(Curry(func)(NODEEVENTNAME));\
					}\

			void GuiVirtualTreeListControl::OnNodeLeftButtonDoubleClick(compositions::GuiGraphicsComposition* sender, compositions::GuiNodeMouseEventArgs& arguments)
			{
				if (arguments.node->GetChildCount() > 0)
				{
					arguments.node->SetExpanding(!arguments.node->GetExpanding());
				}
			}

			GuiVirtualTreeListControl::GuiVirtualTreeListControl(theme::ThemeName themeName, Ptr<tree::INodeRootProvider> _nodeRootProvider)
				:GuiSelectableListControl(themeName, new tree::NodeItemProvider(_nodeRootProvider))
			{
				nodeItemProvider = dynamic_cast<tree::NodeItemProvider*>(GetItemProvider());
				nodeItemView = dynamic_cast<tree::INodeItemView*>(GetItemProvider()->RequestView(tree::INodeItemView::Identifier));

				NodeLeftButtonDown.SetAssociatedComposition(boundsComposition);
				NodeLeftButtonUp.SetAssociatedComposition(boundsComposition);
				NodeLeftButtonDoubleClick.SetAssociatedComposition(boundsComposition);
				NodeMiddleButtonDown.SetAssociatedComposition(boundsComposition);
				NodeMiddleButtonUp.SetAssociatedComposition(boundsComposition);
				NodeMiddleButtonDoubleClick.SetAssociatedComposition(boundsComposition);
				NodeRightButtonDown.SetAssociatedComposition(boundsComposition);
				NodeRightButtonUp.SetAssociatedComposition(boundsComposition);
				NodeRightButtonDoubleClick.SetAssociatedComposition(boundsComposition);
				NodeMouseMove.SetAssociatedComposition(boundsComposition);
				NodeMouseEnter.SetAssociatedComposition(boundsComposition);
				NodeMouseLeave.SetAssociatedComposition(boundsComposition);
				NodeExpanded.SetAssociatedComposition(boundsComposition);
				NodeCollapsed.SetAssociatedComposition(boundsComposition);

				ATTACH_ITEM_MOUSE_EVENT(NodeLeftButtonDown, ItemLeftButtonDown);
				ATTACH_ITEM_MOUSE_EVENT(NodeLeftButtonUp, ItemLeftButtonUp);
				ATTACH_ITEM_MOUSE_EVENT(NodeLeftButtonDoubleClick, ItemLeftButtonDoubleClick);
				ATTACH_ITEM_MOUSE_EVENT(NodeMiddleButtonDown, ItemMiddleButtonDown);
				ATTACH_ITEM_MOUSE_EVENT(NodeMiddleButtonUp, ItemMiddleButtonUp);
				ATTACH_ITEM_MOUSE_EVENT(NodeMiddleButtonDoubleClick, ItemMiddleButtonDoubleClick);
				ATTACH_ITEM_MOUSE_EVENT(NodeRightButtonDown, ItemRightButtonDown);
				ATTACH_ITEM_MOUSE_EVENT(NodeRightButtonUp, ItemRightButtonUp);
				ATTACH_ITEM_MOUSE_EVENT(NodeRightButtonDoubleClick, ItemRightButtonDoubleClick);
				ATTACH_ITEM_MOUSE_EVENT(NodeMouseMove, ItemMouseMove);
				ATTACH_ITEM_NOTIFY_EVENT(NodeMouseEnter, ItemMouseEnter);
				ATTACH_ITEM_NOTIFY_EVENT(NodeMouseLeave, ItemMouseLeave);

				nodeItemProvider->GetRoot()->AttachCallback(this);
				NodeLeftButtonDoubleClick.AttachMethod(this, &GuiVirtualTreeListControl::OnNodeLeftButtonDoubleClick);
			}

#undef ATTACH_ITEM_MOUSE_EVENT
#undef ATTACH_ITEM_NOTIFY_EVENT

			GuiVirtualTreeListControl::~GuiVirtualTreeListControl()
			{
			}

			tree::INodeItemView* GuiVirtualTreeListControl::GetNodeItemView()
			{
				return nodeItemView;
			}

			tree::INodeRootProvider* GuiVirtualTreeListControl::GetNodeRootProvider()
			{
				return nodeItemProvider->GetRoot().Obj();
			}

			namespace tree
			{

/***********************************************************************
TreeViewItem
***********************************************************************/

				const wchar_t* const ITreeViewItemView::Identifier = L"vl::presentation::controls::tree::ITreeViewItemView";

				TreeViewItem::TreeViewItem()
				{
				}

				TreeViewItem::TreeViewItem(const Ptr<GuiImageData>& _image, const WString& _text)
					:image(_image)
					,text(_text)
				{
				}

/***********************************************************************
TreeViewItemRootProvider
***********************************************************************/

				Ptr<GuiImageData> TreeViewItemRootProvider::GetNodeImage(INodeProvider* node)
				{
					MemoryNodeProvider* memoryNode=dynamic_cast<MemoryNodeProvider*>(node);
					if(memoryNode)
					{
						Ptr<TreeViewItem> data=memoryNode->GetData().Cast<TreeViewItem>();
						if(data)
						{
							return data->image;
						}
					}
					return 0;
				}

				WString TreeViewItemRootProvider::GetTextValue(INodeProvider* node)
				{
					MemoryNodeProvider* memoryNode = dynamic_cast<MemoryNodeProvider*>(node);
					if (memoryNode)
					{
						Ptr<TreeViewItem> data = memoryNode->GetData().Cast<TreeViewItem>();
						if (data)
						{
							return data->text;
						}
					}
					return L"";
				}

				description::Value TreeViewItemRootProvider::GetBindingValue(INodeProvider* node)
				{
					return Value::From(GetTreeViewData(node));
				}

				TreeViewItemRootProvider::TreeViewItemRootProvider()
				{
				}

				TreeViewItemRootProvider::~TreeViewItemRootProvider()
				{
				}

				IDescriptable* TreeViewItemRootProvider::RequestView(const WString& identifier)
				{
					if(identifier==ITreeViewItemView::Identifier)
					{
						return (ITreeViewItemView*)this;
					}
					else
					{
						return MemoryNodeRootProvider::RequestView(identifier);
					}
				}

				Ptr<TreeViewItem> TreeViewItemRootProvider::GetTreeViewData(INodeProvider* node)
				{
					MemoryNodeProvider* memoryNode=GetMemoryNode(node);
					if(memoryNode)
					{
						return memoryNode->GetData().Cast<TreeViewItem>();
					}
					else
					{
						return 0;
					}
				}

				void TreeViewItemRootProvider::SetTreeViewData(INodeProvider* node, Ptr<TreeViewItem> value)
				{
					MemoryNodeProvider* memoryNode=GetMemoryNode(node);
					if(memoryNode)
					{
						memoryNode->SetData(value);
					}
				}
				
				void TreeViewItemRootProvider::UpdateTreeViewData(INodeProvider* node)
				{
					MemoryNodeProvider* memoryNode=GetMemoryNode(node);
					if(memoryNode)
					{
						memoryNode->NotifyDataModified();
					}
				}
			}

/***********************************************************************
GuiVirtualTreeView
***********************************************************************/

			templates::GuiTreeItemTemplate* GuiVirtualTreeView::GetStyleFromNode(tree::INodeProvider* node)
			{
				if (itemArranger)
				{
					vint index = nodeItemView->CalculateNodeVisibilityIndex(node);
					if (index != -1)
					{
						auto style = itemArranger->GetVisibleStyle(index);
						return dynamic_cast<templates::GuiTreeItemTemplate*>(style);
					}
				}
				return nullptr;
			}

			void GuiVirtualTreeView::SetStyleExpanding(tree::INodeProvider* node, bool expanding)
			{
				if (auto treeItemStyle = GetStyleFromNode(node))
				{
					treeItemStyle->SetExpanding(expanding);
				}
			}

			void GuiVirtualTreeView::SetStyleExpandable(tree::INodeProvider* node, bool expandable)
			{
				if (auto treeItemStyle = GetStyleFromNode(node))
				{
					treeItemStyle->SetExpandable(expandable);
				}
			}

			void GuiVirtualTreeView::OnAfterItemModified(tree::INodeProvider* parentNode, vint start, vint count, vint newCount)
			{
				GuiVirtualTreeListControl::OnAfterItemModified(parentNode, start, count, newCount);
				SetStyleExpandable(parentNode, parentNode->GetChildCount() > 0);
			}

			void GuiVirtualTreeView::OnItemExpanded(tree::INodeProvider* node)
			{
				GuiVirtualTreeListControl::OnItemExpanded(node);
				SetStyleExpanding(node, true);
			}

			void GuiVirtualTreeView::OnItemCollapsed(tree::INodeProvider* node)
			{
				GuiVirtualTreeListControl::OnItemCollapsed(node);
				SetStyleExpanding(node, false);
			}
			
			void GuiVirtualTreeView::OnStyleInstalled(vint itemIndex, ItemStyle* style)
			{
				GuiVirtualTreeListControl::OnStyleInstalled(itemIndex, style);
				if (auto treeItemStyle = dynamic_cast<templates::GuiTreeItemTemplate*>(style))
				{
					treeItemStyle->SetTextColor(GetControlTemplateObject(true)->GetTextColor());

					if (treeViewItemView)
					{
						if (auto node = nodeItemView->RequestNode(itemIndex))
						{
							treeItemStyle->SetImage(treeViewItemView->GetNodeImage(node.Obj()));
							treeItemStyle->SetExpanding(node->GetExpanding());
							treeItemStyle->SetExpandable(node->GetChildCount() > 0);
							{
								vint level = -1;
								auto current = node;
								while (current->GetParent())
								{
									level++;
									current = current->GetParent();
								}
								treeItemStyle->SetLevel(level);
							}
						}
					}
				}
			}

			GuiVirtualTreeView::GuiVirtualTreeView(theme::ThemeName themeName, Ptr<tree::INodeRootProvider> _nodeRootProvider)
				:GuiVirtualTreeListControl(themeName, _nodeRootProvider)
			{
				treeViewItemView = dynamic_cast<tree::ITreeViewItemView*>(GetNodeRootProvider()->RequestView(tree::ITreeViewItemView::Identifier));
				SetStyleAndArranger(
					[](const Value&) { return new tree::DefaultTreeItemTemplate; },
					new list::FixedHeightItemArranger
				);
			}

			GuiVirtualTreeView::~GuiVirtualTreeView()
			{
			}

/***********************************************************************
GuiTreeView
***********************************************************************/

			GuiTreeView::GuiTreeView(theme::ThemeName themeName)
				:GuiVirtualTreeView(themeName, new tree::TreeViewItemRootProvider)
			{
				nodes = nodeItemProvider->GetRoot().Cast<tree::TreeViewItemRootProvider>();
			}

			GuiTreeView::~GuiTreeView()
			{
			}

			Ptr<tree::TreeViewItemRootProvider> GuiTreeView::Nodes()
			{
				return nodes;
			}

			Ptr<tree::TreeViewItem> GuiTreeView::GetSelectedItem()
			{
				Ptr<tree::TreeViewItem> result;
				vint index = GetSelectedItemIndex();
				if (index != -1)
				{
					if (auto node = nodeItemView->RequestNode(index))
					{
						if (auto memoryNode = node.Cast<tree::MemoryNodeProvider>())
						{
							result = memoryNode->GetData().Cast<tree::TreeViewItem>();
						}
					}
				}
				return result;
			}

			namespace tree
			{

/***********************************************************************
DefaultTreeItemTemplate
***********************************************************************/

				void DefaultTreeItemTemplate::OnInitialize()
				{
					templates::GuiTreeItemTemplate::OnInitialize();
					SetMinSizeLimitation(GuiGraphicsComposition::LimitToElementAndChildren);

					table = new GuiTableComposition;
					AddChild(table);
					table->SetRowsAndColumns(3, 4);
					table->SetRowOption(0, GuiCellOption::PercentageOption(0.5));
					table->SetRowOption(1, GuiCellOption::MinSizeOption());
					table->SetRowOption(2, GuiCellOption::PercentageOption(0.5));
					table->SetColumnOption(0, GuiCellOption::AbsoluteOption(0));
					table->SetColumnOption(1, GuiCellOption::MinSizeOption());
					table->SetColumnOption(2, GuiCellOption::MinSizeOption());
					table->SetColumnOption(3, GuiCellOption::MinSizeOption());
					table->SetAlignmentToParent(Margin(0, 0, 0, 0));
					table->SetCellPadding(2);
					{
						GuiCellComposition* cell = new GuiCellComposition;
						table->AddChild(cell);
						cell->SetSite(0, 1, 3, 1);
						cell->SetPreferredMinSize(Size(16, 16));

						expandingButton = new GuiSelectableButton(theme::ThemeName::TreeItemExpander);
						if (auto treeView = dynamic_cast<GuiVirtualTreeView*>(listControl))
						{
							if (auto expanderStyle = treeView->GetControlTemplateObject(true)->GetExpandingDecoratorTemplate())
							{
								expandingButton->SetControlTemplate(expanderStyle);
							}
						}
						expandingButton->SetAutoSelection(false);
						expandingButton->GetBoundsComposition()->SetAlignmentToParent(Margin(0, 0, 0, 0));
						expandingButton->GetBoundsComposition()->GetEventReceiver()->leftButtonDoubleClick.AttachMethod(this, &DefaultTreeItemTemplate::OnExpandingButtonDoubleClick);
						expandingButton->Clicked.AttachMethod(this, &DefaultTreeItemTemplate::OnExpandingButtonClicked);
						cell->AddChild(expandingButton->GetBoundsComposition());
					}
					{
						GuiCellComposition* cell = new GuiCellComposition;
						table->AddChild(cell);
						cell->SetSite(1, 2, 1, 1);
						cell->SetPreferredMinSize(Size(16, 16));

						imageElement = GuiImageFrameElement::Create();
						imageElement->SetStretch(true);
						cell->SetOwnedElement(imageElement);
					}
					{
						GuiCellComposition* cell = new GuiCellComposition;
						table->AddChild(cell);
						cell->SetSite(0, 3, 3, 1);
						cell->SetPreferredMinSize(Size(192, 0));

						textElement = GuiSolidLabelElement::Create();
						textElement->SetAlignments(Alignment::Left, Alignment::Center);
						textElement->SetEllipse(true);
						cell->SetOwnedElement(textElement);
					}

					FontChanged.AttachMethod(this, &DefaultTreeItemTemplate::OnFontChanged);
					TextChanged.AttachMethod(this, &DefaultTreeItemTemplate::OnTextChanged);
					TextColorChanged.AttachMethod(this, &DefaultTreeItemTemplate::OnTextColorChanged);
					ExpandingChanged.AttachMethod(this, &DefaultTreeItemTemplate::OnExpandingChanged);
					ExpandableChanged.AttachMethod(this, &DefaultTreeItemTemplate::OnExpandableChanged);
					LevelChanged.AttachMethod(this, &DefaultTreeItemTemplate::OnLevelChanged);
					ImageChanged.AttachMethod(this, &DefaultTreeItemTemplate::OnImageChanged);

					FontChanged.Execute(compositions::GuiEventArgs(this));
					TextChanged.Execute(compositions::GuiEventArgs(this));
					TextColorChanged.Execute(compositions::GuiEventArgs(this));
					ExpandingChanged.Execute(compositions::GuiEventArgs(this));
					ExpandableChanged.Execute(compositions::GuiEventArgs(this));
					LevelChanged.Execute(compositions::GuiEventArgs(this));
					ImageChanged.Execute(compositions::GuiEventArgs(this));
				}

				void DefaultTreeItemTemplate::OnFontChanged(compositions::GuiGraphicsComposition* sender, compositions::GuiEventArgs& arguments)
				{
					textElement->SetFont(GetFont());
				}

				void DefaultTreeItemTemplate::OnTextChanged(compositions::GuiGraphicsComposition* sender, compositions::GuiEventArgs& arguments)
				{
					textElement->SetText(GetText());
				}

				void DefaultTreeItemTemplate::OnTextColorChanged(compositions::GuiGraphicsComposition* sender, compositions::GuiEventArgs& arguments)
				{
					textElement->SetColor(GetTextColor());
				}

				void DefaultTreeItemTemplate::OnExpandingChanged(compositions::GuiGraphicsComposition* sender, compositions::GuiEventArgs& arguments)
				{
					expandingButton->SetSelected(GetExpanding());
				}

				void DefaultTreeItemTemplate::OnExpandableChanged(compositions::GuiGraphicsComposition* sender, compositions::GuiEventArgs& arguments)
				{
					expandingButton->SetVisible(GetExpandable());
				}

				void DefaultTreeItemTemplate::OnLevelChanged(compositions::GuiGraphicsComposition* sender, compositions::GuiEventArgs& arguments)
				{
					table->SetColumnOption(0, GuiCellOption::AbsoluteOption(GetLevel() * 12));
				}

				void DefaultTreeItemTemplate::OnImageChanged(compositions::GuiGraphicsComposition* sender, compositions::GuiEventArgs& arguments)
				{
					if (auto imageData = GetImage())
					{
						imageElement->SetImage(imageData->GetImage(), imageData->GetFrameIndex());
					}
					else
					{
						imageElement->SetImage(nullptr);
					}
				}

				void DefaultTreeItemTemplate::OnExpandingButtonDoubleClick(compositions::GuiGraphicsComposition* sender, compositions::GuiMouseEventArgs& arguments)
				{
					arguments.handled = true;
				}

				void DefaultTreeItemTemplate::OnExpandingButtonClicked(compositions::GuiGraphicsComposition* sender, compositions::GuiEventArgs& arguments)
				{
					if (expandingButton->GetVisuallyEnabled())
					{
						if (auto treeControl = dynamic_cast<GuiVirtualTreeListControl*>(listControl))
						{
							if (auto view = treeControl->GetNodeItemView())
							{
								vint index = treeControl->GetArranger()->GetVisibleIndex(this);
								if (index != -1)
								{
									if (auto node = view->RequestNode(index))
									{
										bool expanding = node->GetExpanding();
										node->SetExpanding(!expanding);
									}
								}
							}
						}
					}
				}

				DefaultTreeItemTemplate::DefaultTreeItemTemplate()
				{
				}

				DefaultTreeItemTemplate::~DefaultTreeItemTemplate()
				{
				}
			}
		}
	}
}

/***********************************************************************
.\CONTROLS\GUIDATETIMECONTROLS.CPP
***********************************************************************/

namespace vl
{
	namespace presentation
	{
		namespace controls
		{
			using namespace collections;
			using namespace compositions;
			using namespace elements;

/***********************************************************************
GuiDatePicker::CommandExecutor
***********************************************************************/

			GuiDatePicker::CommandExecutor::CommandExecutor(GuiDatePicker* _datePicker)
				:datePicker(_datePicker)
			{
			}

			GuiDatePicker::CommandExecutor::~CommandExecutor()
			{
			}

			void GuiDatePicker::CommandExecutor::NotifyDateChanged()
			{
				datePicker->date = datePicker->GetControlTemplateObject(true)->GetDate();
				datePicker->UpdateText();
				datePicker->DateChanged.Execute(datePicker->GetNotifyEventArguments());
			}

			void GuiDatePicker::CommandExecutor::NotifyDateNavigated()
			{
				datePicker->DateNavigated.Execute(datePicker->GetNotifyEventArguments());
			}

			void GuiDatePicker::CommandExecutor::NotifyDateSelected()
			{
				datePicker->DateSelected.Execute(datePicker->GetNotifyEventArguments());
			}

/***********************************************************************
GuiDatePicker
***********************************************************************/

			void GuiDatePicker::BeforeControlTemplateUninstalled_()
			{
				auto ct = GetControlTemplateObject(false);
				if (!ct) return;

				ct->SetCommands(nullptr);
			}

			void GuiDatePicker::AfterControlTemplateInstalled_(bool initialize)
			{
				auto ct = GetControlTemplateObject(true);
				ct->SetCommands(commandExecutor.Obj());
				ct->SetDate(date);
				ct->SetDateLocale(dateLocale);
				UpdateText();
			}

			void GuiDatePicker::UpdateText()
			{
				GuiControl::SetText(dateLocale.FormatDate(dateFormat, date));
			}

			GuiDatePicker::GuiDatePicker(theme::ThemeName themeName)
				:GuiControl(themeName)
			{
				commandExecutor = new CommandExecutor(this);
				SetDateLocale(Locale::UserDefault());
				SetDate(DateTime::LocalTime());

				DateChanged.SetAssociatedComposition(boundsComposition);
				DateNavigated.SetAssociatedComposition(boundsComposition);
				DateSelected.SetAssociatedComposition(boundsComposition);
				DateFormatChanged.SetAssociatedComposition(boundsComposition);
				DateLocaleChanged.SetAssociatedComposition(boundsComposition);

				commandExecutor->NotifyDateChanged();
			}

			GuiDatePicker::~GuiDatePicker()
			{
			}

			const DateTime& GuiDatePicker::GetDate()
			{
				return date;
			}

			void GuiDatePicker::SetDate(const DateTime& value)
			{
				if (date != value)
				{
					date = value;
					GetControlTemplateObject(true)->SetDate(value);
				}
			}

			const WString& GuiDatePicker::GetDateFormat()
			{
				return dateFormat;
			}

			void GuiDatePicker::SetDateFormat(const WString& value)
			{
				dateFormat=value;
				UpdateText();
				DateFormatChanged.Execute(GetNotifyEventArguments());
			}

			const Locale& GuiDatePicker::GetDateLocale()
			{
				return dateLocale;
			}

			void GuiDatePicker::SetDateLocale(const Locale& value)
			{
				dateLocale=value;
				List<WString> formats;
				dateLocale.GetLongDateFormats(formats);
				if(formats.Count()>0)
				{
					dateFormat=formats[0];
				}
				GetControlTemplateObject(true)->SetDateLocale(dateLocale);

				UpdateText();
				DateFormatChanged.Execute(GetNotifyEventArguments());
				DateLocaleChanged.Execute(GetNotifyEventArguments());
			}

			void GuiDatePicker::SetText(const WString& value)
			{
			}

/***********************************************************************
GuiDateComboBox
***********************************************************************/

			void GuiDateComboBox::UpdateText()
			{
				SetText(datePicker->GetDateLocale().FormatDate(datePicker->GetDateFormat(), selectedDate));
			}

			void GuiDateComboBox::NotifyUpdateSelectedDate()
			{
				UpdateText();
				SelectedDateChanged.Execute(GetNotifyEventArguments());
			}

			void GuiDateComboBox::OnSubMenuOpeningChanged(compositions::GuiGraphicsComposition* sender, compositions::GuiEventArgs& arguments)
			{
				datePicker->SetDate(selectedDate);
			}

			void GuiDateComboBox::datePicker_DateLocaleChanged(compositions::GuiGraphicsComposition* sender, compositions::GuiEventArgs& arguments)
			{
				UpdateText();
			}

			void GuiDateComboBox::datePicker_DateFormatChanged(compositions::GuiGraphicsComposition* sender, compositions::GuiEventArgs& arguments)
			{
				UpdateText();
			}

			void GuiDateComboBox::datePicker_DateSelected(compositions::GuiGraphicsComposition* sender, compositions::GuiEventArgs& arguments)
			{
				selectedDate=datePicker->GetDate();
				GetSubMenu()->Hide();
				SelectItem();
				NotifyUpdateSelectedDate();
			}

			GuiDateComboBox::GuiDateComboBox(theme::ThemeName themeName, GuiDatePicker* _datePicker)
				:GuiComboBoxBase(themeName)
				,datePicker(_datePicker)
			{
				SelectedDateChanged.SetAssociatedComposition(GetBoundsComposition());

				datePicker->DateSelected.AttachMethod(this, &GuiDateComboBox::datePicker_DateSelected);
				datePicker->DateLocaleChanged.AttachMethod(this, &GuiDateComboBox::datePicker_DateLocaleChanged);
				datePicker->DateFormatChanged.AttachMethod(this, &GuiDateComboBox::datePicker_DateFormatChanged);
				datePicker->GetBoundsComposition()->SetAlignmentToParent(Margin(0, 0, 0, 0));
				GetSubMenu()->GetContainerComposition()->AddChild(datePicker->GetBoundsComposition());

				selectedDate=datePicker->GetDate();
				SubMenuOpeningChanged.AttachMethod(this, &GuiDateComboBox::OnSubMenuOpeningChanged);
				SetFont(GetFont());
				SetText(datePicker->GetText());
			}

			GuiDateComboBox::~GuiDateComboBox()
			{
			}

			void GuiDateComboBox::SetFont(const FontProperties& value)
			{
				GuiComboBoxBase::SetFont(value);
				datePicker->SetFont(value);
			}

			const DateTime& GuiDateComboBox::GetSelectedDate()
			{
				return selectedDate;
			}

			void GuiDateComboBox::SetSelectedDate(const DateTime& value)
			{
				selectedDate=value;
				NotifyUpdateSelectedDate();
			}

			GuiDatePicker* GuiDateComboBox::GetDatePicker()
			{
				return datePicker;
			}
		}
	}
}

/***********************************************************************
.\CONTROLS\LISTCONTROLPACKAGE\GUICOMBOCONTROLS.CPP
***********************************************************************/

namespace vl
{
	namespace presentation
	{
		namespace controls
		{

/***********************************************************************
GuiComboBoxBase::CommandExecutor
***********************************************************************/

			GuiComboBoxBase::CommandExecutor::CommandExecutor(GuiComboBoxBase* _combo)
				:combo(_combo)
			{
			}

			GuiComboBoxBase::CommandExecutor::~CommandExecutor()
			{
			}

			void GuiComboBoxBase::CommandExecutor::SelectItem()
			{
				combo->SelectItem();
			}

/***********************************************************************
GuiComboBoxBase
***********************************************************************/

			void GuiComboBoxBase::BeforeControlTemplateUninstalled_()
			{
				auto ct = GetControlTemplateObject(false);
				if (!ct) return;

				ct->SetCommands(nullptr);
			}

			void GuiComboBoxBase::AfterControlTemplateInstalled_(bool initialize)
			{
				GetControlTemplateObject(true)->SetCommands(commandExecutor.Obj());
			}

			bool GuiComboBoxBase::IsAltAvailable()
			{
				return false;
			}

			IGuiMenuService::Direction GuiComboBoxBase::GetSubMenuDirection()
			{
				return IGuiMenuService::Horizontal;
			}

			void GuiComboBoxBase::SelectItem()
			{
				ItemSelected.Execute(GetNotifyEventArguments());
			}

			void GuiComboBoxBase::OnBoundsChanged(compositions::GuiGraphicsComposition* sender, compositions::GuiEventArgs& arguments)
			{
				Size size=GetPreferredMenuClientSize();
				size.x=boundsComposition->GetBounds().Width();
				SetPreferredMenuClientSize(size);
			}

			GuiComboBoxBase::GuiComboBoxBase(theme::ThemeName themeName)
				:GuiMenuButton(themeName)
			{
				commandExecutor = new CommandExecutor(this);

				CreateSubMenu();
				SetCascadeAction(false);

				boundsComposition->BoundsChanged.AttachMethod(this, &GuiComboBoxBase::OnBoundsChanged);
			}

			GuiComboBoxBase::~GuiComboBoxBase()
			{
			}

/***********************************************************************
GuiComboBoxListControl
***********************************************************************/

			void GuiComboBoxListControl::BeforeControlTemplateUninstalled()
			{
				GuiComboBoxBase::BeforeControlTemplateUninstalled();
			}

			void GuiComboBoxListControl::AfterControlTemplateInstalled(bool initialize)
			{
				GuiComboBoxBase::AfterControlTemplateInstalled(initialize);
				GetControlTemplateObject(true)->SetTextVisible(!itemStyleProperty);
			}

			bool GuiComboBoxListControl::IsAltAvailable()
			{
				return true;
			}

			void GuiComboBoxListControl::OnActiveAlt()
			{
				GuiMenuButton::OnActiveAlt();
				GetSubMenu()->GetNativeWindow()->SetFocus();
				containedListControl->SetFocus();
			}

			void GuiComboBoxListControl::RemoveStyleController()
			{
				if (itemStyleController)
				{
					SafeDeleteComposition(itemStyleController);
					itemStyleController = nullptr;
				}
			}

			void GuiComboBoxListControl::InstallStyleController(vint itemIndex)
			{
				if (itemStyleProperty)
				{
					if (itemIndex != -1)
					{
						auto item = containedListControl->GetItemProvider()->GetBindingValue(itemIndex);
						if (!item.IsNull())
						{
							if (auto style = itemStyleProperty(item))
							{
								itemStyleController = style;
								itemStyleController->SetText(GetText());
								itemStyleController->SetFont(GetFont());
								itemStyleController->SetContext(GetContext());
								itemStyleController->SetVisuallyEnabled(GetVisuallyEnabled());
								itemStyleController->SetAlignmentToParent(Margin(0, 0, 0, 0));
								containerComposition->AddChild(itemStyleController);
							}
						}
					}
				}
			}

			void GuiComboBoxListControl::DisplaySelectedContent(vint itemIndex)
			{
				if(itemIndex==-1)
				{
					SetText(L"");
				}
				else
				{
					WString text = containedListControl->GetItemProvider()->GetTextValue(itemIndex);
					SetText(text);
					GetSubMenu()->Hide();
				}

				RemoveStyleController();
				InstallStyleController(itemIndex);
			}

			void GuiComboBoxListControl::AdoptSubMenuSize()
			{
				Size expectedSize(0, GetFont().size * 20);
				Size adoptedSize = containedListControl->GetAdoptedSize(expectedSize);

				Size clientSize = GetPreferredMenuClientSize();
				clientSize.y = adoptedSize.y + GetSubMenu()->GetClientSize().y - containedListControl->GetBoundsComposition()->GetBounds().Height();
				SetPreferredMenuClientSize(clientSize);

				if (GetSubMenuOpening())
				{
					GetSubMenu()->SetClientSize(clientSize);
				}
			}

			void GuiComboBoxListControl::OnTextChanged(compositions::GuiGraphicsComposition* sender, compositions::GuiEventArgs& arguments)
			{
				if (itemStyleController)
				{
					itemStyleController->SetText(GetText());
				}
			}

			void GuiComboBoxListControl::OnFontChanged(compositions::GuiGraphicsComposition* sender, compositions::GuiEventArgs& arguments)
			{
				if (itemStyleController)
				{
					itemStyleController->SetFont(GetFont());
				}
				AdoptSubMenuSize();
			}

			void GuiComboBoxListControl::OnContextChanged(compositions::GuiGraphicsComposition* sender, compositions::GuiEventArgs& arguments)
			{
				if (itemStyleController)
				{
					itemStyleController->SetContext(GetContext());
				}
				AdoptSubMenuSize();
			}

			void GuiComboBoxListControl::OnVisuallyEnabledChanged(compositions::GuiGraphicsComposition* sender, compositions::GuiEventArgs& arguments)
			{
				if (itemStyleController)
				{
					itemStyleController->SetVisuallyEnabled(GetVisuallyEnabled());
				}
			}

			void GuiComboBoxListControl::OnListControlAdoptedSizeInvalidated(compositions::GuiGraphicsComposition* sender, compositions::GuiEventArgs& arguments)
			{
				AdoptSubMenuSize();
			}

			void GuiComboBoxListControl::OnListControlBoundsChanged(compositions::GuiGraphicsComposition* sender, compositions::GuiEventArgs& arguments)
			{
				GetApplication()->InvokeLambdaInMainThread(GetRelatedControlHost(), [=]()
				{
					AdoptSubMenuSize();
				});
			}

			void GuiComboBoxListControl::OnListControlSelectionChanged(compositions::GuiGraphicsComposition* sender, compositions::GuiEventArgs& arguments)
			{
				DisplaySelectedContent(GetSelectedIndex());
				SelectItem();
				SelectedIndexChanged.Execute(GetNotifyEventArguments());
			}

			GuiComboBoxListControl::GuiComboBoxListControl(theme::ThemeName themeName, GuiSelectableListControl* _containedListControl)
				:GuiComboBoxBase(themeName)
				, containedListControl(_containedListControl)
			{
				TextChanged.AttachMethod(this, &GuiComboBoxListControl::OnTextChanged);
				FontChanged.AttachMethod(this, &GuiComboBoxListControl::OnFontChanged);
				ContextChanged.AttachMethod(this, &GuiComboBoxListControl::OnContextChanged);
				VisuallyEnabledChanged.AttachMethod(this, &GuiComboBoxListControl::OnVisuallyEnabledChanged);

				containedListControl->SetMultiSelect(false);
				containedListControl->AdoptedSizeInvalidated.AttachMethod(this, &GuiComboBoxListControl::OnListControlAdoptedSizeInvalidated);
				containedListControl->SelectionChanged.AttachMethod(this, &GuiComboBoxListControl::OnListControlSelectionChanged);
				boundsChangedHandler = containedListControl->GetBoundsComposition()->BoundsChanged.AttachMethod(this, &GuiComboBoxListControl::OnListControlBoundsChanged);

				auto itemProvider = containedListControl->GetItemProvider();

				SelectedIndexChanged.SetAssociatedComposition(boundsComposition);

				containedListControl->GetBoundsComposition()->SetAlignmentToParent(Margin(0, 0, 0, 0));
				GetSubMenu()->GetContainerComposition()->AddChild(containedListControl->GetBoundsComposition());
				SetFont(GetFont());
			}

			GuiComboBoxListControl::~GuiComboBoxListControl()
			{
				containedListControl->GetBoundsComposition()->BoundsChanged.Detach(boundsChangedHandler);
				boundsChangedHandler = nullptr;
			}

			GuiSelectableListControl* GuiComboBoxListControl::GetContainedListControl()
			{
				return containedListControl;
			}

			GuiComboBoxListControl::ItemStyleProperty GuiComboBoxListControl::GetItemTemplate()
			{
				return itemStyleProperty;
			}

			void GuiComboBoxListControl::SetItemTemplate(ItemStyleProperty value)
			{
				RemoveStyleController();
				itemStyleProperty = value;
				GetControlTemplateObject(true)->SetTextVisible(!itemStyleProperty);
				InstallStyleController(GetSelectedIndex());
				ItemTemplateChanged.Execute(GetNotifyEventArguments());
			}

			vint GuiComboBoxListControl::GetSelectedIndex()
			{
				if(containedListControl->GetSelectedItems().Count()==1)
				{
					return containedListControl->GetSelectedItems()[0];
				}
				else
				{
					return -1;
				}
			}

			void GuiComboBoxListControl::SetSelectedIndex(vint value)
			{
				containedListControl->SetSelected(value, true);
			}

			description::Value GuiComboBoxListControl::GetSelectedItem()
			{
				auto selectedIndex = GetSelectedIndex();
				if (selectedIndex != -1)
				{
					return containedListControl->GetItemProvider()->GetBindingValue(selectedIndex);
				}
				return description::Value();
			}

			GuiListControl::IItemProvider* GuiComboBoxListControl::GetItemProvider()
			{
				return containedListControl->GetItemProvider();
			}
		}
	}
}


/***********************************************************************
.\CONTROLS\TEMPLATES\GUICOMMONTEMPLATES.CPP
***********************************************************************/

namespace vl
{
	namespace presentation
	{
		namespace templates
		{
			using namespace elements;
			using namespace compositions;
			using namespace templates;
			using namespace controls;
			using namespace theme;

/***********************************************************************
GuiCommonDatePickerLook
***********************************************************************/

			vint GetDayCountForMonth(vint year, vint month)
			{
				bool isLeapYear = (year % 100 == 0) ? (year % 400 == 0) : (year % 4 == 0);
				switch (month)
				{
				case 1:case 3:case 5:case 7:case 8:case 10:case 12:
					return 31;
				case 4:case 6:case 9:case 11:
					return 30;
				default:
					return isLeapYear ? 29 : 28;
				}
			}

			void StepPreviousMonth(vint& year, vint& month)
			{
				if (month == 1)
				{
					year--;
					month = 12;
				}
				else
				{
					month--;
				}
			}

			void StepNextMonth(vint& year, vint& month)
			{
				if (month == 12)
				{
					year++;
					month = 1;
				}
				else
				{
					month++;
				}
			}

			void GuiCommonDatePickerLook::SetDay(const DateTime& day, vint& index, bool currentMonth)
			{
				dateDays[index] = day;
				GuiSolidLabelElement* label = labelDays[index];
				label->SetText(itow(day.day));
				label->SetColor(currentMonth ? primaryTextColor : secondaryTextColor);
				index++;
			}

			void GuiCommonDatePickerLook::comboYearMonth_SelectedIndexChanged(compositions::GuiGraphicsComposition* sender, compositions::GuiEventArgs& arguments)
			{
				if (!preventComboEvent)
				{
					if (comboYear->GetSelectedIndex() != -1 && comboMonth->GetSelectedIndex() != -1)
					{
						vint year = comboYear->GetSelectedIndex() + YearFirst;
						vint month = comboMonth->GetSelectedIndex() + 1;
						SetDate(DateTime::FromDateTime(year, month, 1));

						GuiEventArgs arguments(this);
						DateChanged.Execute(arguments);
						commands->NotifyDateChanged();
						commands->NotifyDateNavigated();
					}
				}
			}

			void GuiCommonDatePickerLook::buttonDay_SelectedChanged(compositions::GuiGraphicsComposition* sender, compositions::GuiEventArgs& arguments)
			{
				if (!preventButtonEvent)
				{
					GuiSelectableButton* button = dynamic_cast<GuiSelectableButton*>(sender->GetRelatedControl());
					if (button->GetSelected())
					{
						vint index = buttonDays.IndexOf(button);
						if (index != -1)
						{
							DateTime day = dateDays[index];
							if (day.year != currentDate.year || day.month != currentDate.month)
							{
								SetDate(day);
							}
							else
							{
								currentDate = day;
							}

							GuiEventArgs arguments(this);
							DateChanged.Execute(arguments);
							commands->NotifyDateChanged();
							commands->NotifyDateSelected();
						}
					}
				}
			}

			void GuiCommonDatePickerLook::DisplayMonth(vint year, vint month)
			{
				if (YearFirst <= year && year <= YearLast && 1 <= month && month <= 12)
				{
					preventComboEvent = true;
					comboYear->SetSelectedIndex(year - YearFirst);
					comboMonth->SetSelectedIndex(month - 1);
					preventComboEvent = false;
				}

				vint yearPrev = year, yearNext = year, monthPrev = month, monthNext = month;
				StepPreviousMonth(yearPrev, monthPrev);
				StepNextMonth(yearNext, monthNext);

				vint countPrev = GetDayCountForMonth(yearPrev, monthPrev);
				vint count = GetDayCountForMonth(year, month);
				vint countNext = GetDayCountForMonth(yearNext, monthNext);

				DateTime firstDay = DateTime::FromDateTime(year, month, 1);
				vint showPrev = firstDay.dayOfWeek;
				if (showPrev == 0) showPrev = DaysOfWeek;
				vint show = count;
				vint showNext = DaysOfWeek*DayRows - showPrev - show;

				vint index = 0;
				for (vint i = 0; i < showPrev; i++)
				{
					DateTime day = DateTime::FromDateTime(yearPrev, monthPrev, countPrev - (showPrev - i - 1));
					SetDay(day, index, false);
				}
				for (vint i = 0; i < show; i++)
				{
					DateTime day = DateTime::FromDateTime(year, month, i + 1);
					SetDay(day, index, true);
				}
				for (vint i = 0; i < showNext; i++)
				{
					DateTime day = DateTime::FromDateTime(yearNext, monthNext, i + 1);
					SetDay(day, index, false);
				}
			}

			void GuiCommonDatePickerLook::SelectDay(vint day)
			{
				for (vint i = 0; i < dateDays.Count(); i++)
				{
					const DateTime& dt = dateDays[i];
					if (dt.year == currentDate.year && dt.month == currentDate.month && dt.day == day)
					{
						preventButtonEvent = true;
						buttonDays[i]->SetSelected(true);
						preventButtonEvent = false;
						break;
					}
				}
			}

			GuiCommonDatePickerLook::GuiCommonDatePickerLook(Color _backgroundColor, Color _primaryTextColor, Color _secondaryTextColor)
				:backgroundColor(_backgroundColor)
				, primaryTextColor(_primaryTextColor)
				, secondaryTextColor(_secondaryTextColor)
			{
				DateChanged.SetAssociatedComposition(this);
				SetMinSizeLimitation(GuiGraphicsComposition::LimitToElementAndChildren);

				GuiTableComposition* monthTable = 0;
				GuiTableComposition* dayTable = 0;
				{
					listYears = new GuiTextList(theme::ThemeName::TextList);
					listYears->SetHorizontalAlwaysVisible(false);
					for (vint i = YearFirst; i <= YearLast; i++)
					{
						listYears->GetItems().Add(new list::TextItem(itow(i)));
					}
					comboYear = new GuiComboBoxListControl(theme::ThemeName::ComboBox, listYears);
					comboYear->GetBoundsComposition()->SetAlignmentToParent(Margin(0, 0, 2, 0));
					comboYear->SelectedIndexChanged.AttachMethod(this, &GuiCommonDatePickerLook::comboYearMonth_SelectedIndexChanged);
				}
				{
					listMonths = new GuiTextList(theme::ThemeName::TextList);
					listMonths->SetHorizontalAlwaysVisible(false);
					comboMonth = new GuiComboBoxListControl(theme::ThemeName::ComboBox, listMonths);
					comboMonth->GetBoundsComposition()->SetAlignmentToParent(Margin(2, 0, 0, 0));
					comboMonth->SelectedIndexChanged.AttachMethod(this, &GuiCommonDatePickerLook::comboYearMonth_SelectedIndexChanged);
				}
				{
					monthTable = new GuiTableComposition;
					monthTable->SetAlignmentToParent(Margin(0, 0, 0, 0));
					monthTable->SetMinSizeLimitation(GuiGraphicsComposition::LimitToElementAndChildren);
					monthTable->SetRowsAndColumns(1, 2);
					monthTable->SetRowOption(0, GuiCellOption::MinSizeOption());
					monthTable->SetColumnOption(0, GuiCellOption::PercentageOption(0.5));
					monthTable->SetColumnOption(1, GuiCellOption::PercentageOption(0.5));
					{
						GuiCellComposition* cell = new GuiCellComposition;
						monthTable->AddChild(cell);
						cell->SetSite(0, 0, 1, 1);
						cell->AddChild(comboYear->GetBoundsComposition());
					}
					{
						GuiCellComposition* cell = new GuiCellComposition;
						monthTable->AddChild(cell);
						cell->SetSite(0, 1, 1, 1);
						cell->AddChild(comboMonth->GetBoundsComposition());
					}
				}
				{
					dayTable = new GuiTableComposition;
					dayTable->SetMinSizeLimitation(GuiGraphicsComposition::LimitToElementAndChildren);
					dayTable->SetCellPadding(4);
					dayTable->SetRowsAndColumns(DayRows + DayRowStart, DaysOfWeek);

					for (vint i = 0; i < DayRowStart; i++)
					{
						dayTable->SetRowOption(i, GuiCellOption::MinSizeOption());
					}
					for (vint i = 0; i < DayRows; i++)
					{
						dayTable->SetRowOption(i + DayRowStart, GuiCellOption::PercentageOption(1.0));
					}
					for (vint i = 0; i < DaysOfWeek; i++)
					{
						dayTable->SetColumnOption(i, GuiCellOption::PercentageOption(1.0));
					}

					{
						GuiCellComposition* cell = new GuiCellComposition;
						dayTable->AddChild(cell);
						cell->SetSite(0, 0, 1, DaysOfWeek);
						cell->AddChild(monthTable);
					}

					labelDaysOfWeek.Resize(7);
					for (vint i = 0; i < DaysOfWeek; i++)
					{
						GuiCellComposition* cell = new GuiCellComposition;
						dayTable->AddChild(cell);
						cell->SetSite(1, i, 1, 1);

						GuiSolidLabelElement* element = GuiSolidLabelElement::Create();
						element->SetAlignments(Alignment::Center, Alignment::Center);
						element->SetColor(primaryTextColor);
						labelDaysOfWeek[i] = element;
						cell->SetOwnedElement(element);
					}

					buttonDays.Resize(DaysOfWeek*DayRows);
					labelDays.Resize(DaysOfWeek*DayRows);
					dateDays.Resize(DaysOfWeek*DayRows);

					auto dayMutexController = new GuiSelectableButton::MutexGroupController;
					AddComponent(dayMutexController);

					for (vint i = 0; i < DaysOfWeek; i++)
					{
						for (vint j = 0; j < DayRows; j++)
						{
							GuiCellComposition* cell = new GuiCellComposition;
							dayTable->AddChild(cell);
							cell->SetSite(j + DayRowStart, i, 1, 1);

							GuiSelectableButton* button = new GuiSelectableButton(theme::ThemeName::CheckBox);
							button->GetBoundsComposition()->SetAlignmentToParent(Margin(0, 0, 0, 0));
							button->SetGroupController(dayMutexController);
							button->SelectedChanged.AttachMethod(this, &GuiCommonDatePickerLook::buttonDay_SelectedChanged);
							cell->AddChild(button->GetBoundsComposition());
							buttonDays[j*DaysOfWeek + i] = button;

							GuiSolidLabelElement* element = GuiSolidLabelElement::Create();
							element->SetAlignments(Alignment::Center, Alignment::Center);
							element->SetText(L"0");
							labelDays[j*DaysOfWeek + i] = element;

							GuiBoundsComposition* elementBounds = new GuiBoundsComposition;
							elementBounds->SetOwnedElement(element);
							elementBounds->SetAlignmentToParent(Margin(0, 0, 0, 0));
							elementBounds->SetMinSizeLimitation(GuiGraphicsComposition::LimitToElement);
							button->GetContainerComposition()->AddChild(elementBounds);
						}
					}
				}
				{
					GuiSolidBackgroundElement* element = GuiSolidBackgroundElement::Create();
					element->SetColor(backgroundColor);
					dayTable->SetOwnedElement(element);
				}

				dayTable->SetAlignmentToParent(Margin(0, 0, 0, 0));
				AddChild(dayTable);

				SetMinSizeLimitation(GuiGraphicsComposition::LimitToElementAndChildren);
				SetFont(font);
			}

			GuiCommonDatePickerLook::~GuiCommonDatePickerLook()
			{
				FinalizeInstanceRecursively(this);
			}

			controls::IDatePickerCommandExecutor* GuiCommonDatePickerLook::GetCommands()
			{
				return commands;
			}

			void GuiCommonDatePickerLook::SetCommands(controls::IDatePickerCommandExecutor* value)
			{
				commands = value;
			}

			TemplateProperty<GuiSelectableButtonTemplate> GuiCommonDatePickerLook::GetDateButtonTemplate()
			{
				return dateButtonTemplate;
			}

			void GuiCommonDatePickerLook::SetDateButtonTemplate(const TemplateProperty<GuiSelectableButtonTemplate>& value)
			{
				dateButtonTemplate = value;
				FOREACH(GuiSelectableButton*, button, buttonDays)
				{
					button->SetControlTemplate(value);
				}
			}

			TemplateProperty<GuiTextListTemplate> GuiCommonDatePickerLook::GetDateTextListTemplate()
			{
				return dateTextListTemplate;
			}

			void GuiCommonDatePickerLook::SetDateTextListTemplate(const TemplateProperty<GuiTextListTemplate>& value)
			{
				dateTextListTemplate = value;
				listYears->SetControlTemplate(value);
				listMonths->SetControlTemplate(value);
			}

			TemplateProperty<GuiComboBoxTemplate> GuiCommonDatePickerLook::GetDateComboBoxTemplate()
			{
				return dateComboBoxTemplate;
			}

			void GuiCommonDatePickerLook::SetDateComboBoxTemplate(const TemplateProperty<GuiComboBoxTemplate>& value)
			{
				dateComboBoxTemplate = value;
				comboYear->SetControlTemplate(value);
				comboMonth->SetControlTemplate(value);
			}

			const Locale& GuiCommonDatePickerLook::GetDateLocale()
			{
				return dateLocale;
			}

			void GuiCommonDatePickerLook::SetDateLocale(const Locale& value)
			{
				if (dateLocale != value)
				{
					dateLocale = value;
					for (vint i = 0; i < DaysOfWeek; i++)
					{
						labelDaysOfWeek[i]->SetText(dateLocale.GetShortDayOfWeekName(i));
					}

					listMonths->GetItems().Clear();
					for (vint i = 1; i <= 12; i++)
					{
						listMonths->GetItems().Add(new list::TextItem(dateLocale.GetLongMonthName(i)));
					}

					SetDate(currentDate);
				}
			}

			const DateTime& GuiCommonDatePickerLook::GetDate()
			{
				return currentDate;
			}

			void GuiCommonDatePickerLook::SetDate(const DateTime& value)
			{
				currentDate = value;
				DisplayMonth(value.year, value.month);
				SelectDay(value.day);
			}

			const FontProperties& GuiCommonDatePickerLook::GetFont()
			{
				return font;
			}

			void GuiCommonDatePickerLook::SetFont(const FontProperties& value)
			{
				if (font != value)
				{
					font = value;
					comboYear->SetFont(value);
					listYears->SetFont(value);
					comboMonth->SetFont(value);
					listMonths->SetFont(value);
					FOREACH(GuiSolidLabelElement*, label, From(labelDaysOfWeek).Concat(labelDays))
					{
						label->SetFont(value);
					}
				}
			}

/***********************************************************************
GuiCommonScrollViewLook
***********************************************************************/

			void GuiCommonScrollViewLook::UpdateTable()
			{
				if (horizontalScroll->GetVisible())
				{
					tableComposition->SetRowOption(1, GuiCellOption::AbsoluteOption(defaultScrollSize));
				}
				else
				{
					tableComposition->SetRowOption(1, GuiCellOption::AbsoluteOption(0));
				}

				if (verticalScroll->GetVisible())
				{
					tableComposition->SetColumnOption(1, GuiCellOption::AbsoluteOption(defaultScrollSize));
				}
				else
				{
					tableComposition->SetColumnOption(1, GuiCellOption::AbsoluteOption(0));
				}

				tableComposition->UpdateCellBounds();
			}

			void GuiCommonScrollViewLook::hScroll_OnVisibleChanged(compositions::GuiGraphicsComposition* sender, compositions::GuiEventArgs& arguments)
			{
				UpdateTable();
			}

			void GuiCommonScrollViewLook::vScroll_OnVisibleChanged(compositions::GuiGraphicsComposition* sender, compositions::GuiEventArgs& arguments)
			{
				UpdateTable();
			}

			GuiCommonScrollViewLook::GuiCommonScrollViewLook(vint _defaultScrollSize)
				:defaultScrollSize(_defaultScrollSize)
			{
				SetMinSizeLimitation(GuiGraphicsComposition::LimitToElementAndChildren);

				horizontalScroll = new GuiScroll(theme::ThemeName::HScroll);
				horizontalScroll->GetBoundsComposition()->SetAlignmentToParent(Margin(0, 0, 0, 0));
				horizontalScroll->SetEnabled(false);
				verticalScroll = new GuiScroll(theme::ThemeName::VScroll);
				verticalScroll->GetBoundsComposition()->SetAlignmentToParent(Margin(0, 0, 0, 0));
				verticalScroll->SetEnabled(false);

				tableComposition = new GuiTableComposition;
				AddChild(tableComposition);
				tableComposition->SetAlignmentToParent(Margin(0, 0, 0, 0));
				tableComposition->SetRowsAndColumns(2, 2);
				tableComposition->SetRowOption(0, GuiCellOption::PercentageOption(1.0));
				tableComposition->SetRowOption(1, GuiCellOption::MinSizeOption());
				tableComposition->SetColumnOption(0, GuiCellOption::PercentageOption(1.0));
				tableComposition->SetColumnOption(1, GuiCellOption::MinSizeOption());
				UpdateTable();
				{
					GuiCellComposition* cell = new GuiCellComposition;
					tableComposition->AddChild(cell);
					cell->SetSite(1, 0, 1, 1);
					cell->AddChild(horizontalScroll->GetBoundsComposition());
				}
				{
					GuiCellComposition* cell = new GuiCellComposition;
					tableComposition->AddChild(cell);
					cell->SetSite(0, 1, 1, 1);
					cell->AddChild(verticalScroll->GetBoundsComposition());
				}

				containerCellComposition = new GuiCellComposition;
				tableComposition->AddChild(containerCellComposition);
				containerCellComposition->SetSite(0, 0, 1, 1);

				containerComposition = new GuiBoundsComposition;
				containerComposition->SetAlignmentToParent(Margin(0, 0, 0, 0));
				containerCellComposition->AddChild(containerComposition);

				horizontalScroll->VisibleChanged.AttachMethod(this, &GuiCommonScrollViewLook::hScroll_OnVisibleChanged);
				verticalScroll->VisibleChanged.AttachMethod(this, &GuiCommonScrollViewLook::vScroll_OnVisibleChanged);
				UpdateTable();
			}

			GuiCommonScrollViewLook::~GuiCommonScrollViewLook()
			{
			}

			controls::GuiScroll* GuiCommonScrollViewLook::GetHScroll()
			{
				return horizontalScroll;
			}

			controls::GuiScroll* GuiCommonScrollViewLook::GetVScroll()
			{
				return verticalScroll;
			}

			compositions::GuiGraphicsComposition* GuiCommonScrollViewLook::GetContainerComposition()
			{
				return containerComposition;
			}

			TemplateProperty<GuiScrollTemplate> GuiCommonScrollViewLook::GetHScrollTemplate()
			{
				return hScrollTemplate;
			}

			void GuiCommonScrollViewLook::SetHScrollTemplate(const TemplateProperty<GuiScrollTemplate>& value)
			{
				hScrollTemplate = value;
				horizontalScroll->SetControlTemplate(value);
			}

			TemplateProperty<GuiScrollTemplate> GuiCommonScrollViewLook::GetVScrollTemplate()
			{
				return vScrollTemplate;
			}

			void GuiCommonScrollViewLook::SetVScrollTemplate(const TemplateProperty<GuiScrollTemplate>& value)
			{
				vScrollTemplate = value;
				verticalScroll->SetControlTemplate(value);
			}

/***********************************************************************
GuiCommonScrollBehavior
***********************************************************************/

			void GuiCommonScrollBehavior::SetScroll(vint totalPixels, vint newOffset)
			{
				vint totalSize = scrollTemplate->GetTotalSize();
				double ratio = (double)newOffset / totalPixels;
				vint newPosition = (vint)round(ratio * totalSize);

				vint offset1 = (vint)round(((double)newPosition / totalSize) * totalPixels);
				vint offset2 = (vint)round(((double)(newPosition + 1)) / totalSize * totalPixels);
				vint delta1 = offset1 - newOffset;
				vint delta2 = offset2 - newOffset;

				if (delta1 < 0) { delta1 = -delta1; }
				if (delta2 < 0) { delta2 = -delta2; }

				if (delta1 < delta2)
				{
					scrollTemplate->GetCommands()->SetPosition(newPosition);
				}
				else
				{
					scrollTemplate->GetCommands()->SetPosition(newPosition + 1);
				}
			}

			void GuiCommonScrollBehavior::AttachHandle(compositions::GuiGraphicsComposition* handle)
			{
				handle->GetEventReceiver()->leftButtonDown.AttachLambda([=](GuiGraphicsComposition*, GuiMouseEventArgs& arguments)
				{
					if (scrollTemplate->GetVisuallyEnabled())
					{
						dragging = true;
						location.x = arguments.x;
						location.y = arguments.y;
					}
				});

				handle->GetEventReceiver()->leftButtonUp.AttachLambda([=](GuiGraphicsComposition*, GuiMouseEventArgs&)
				{
					if (scrollTemplate->GetVisuallyEnabled())
					{
						dragging = false;
					}
				});
			}

			GuiCommonScrollBehavior::GuiCommonScrollBehavior()
			{
			}

			GuiCommonScrollBehavior::~GuiCommonScrollBehavior()
			{
			}

			void GuiCommonScrollBehavior::AttachScrollTemplate(GuiScrollTemplate* value)
			{
				scrollTemplate = value;
			}

			void GuiCommonScrollBehavior::AttachDecreaseButton(controls::GuiButton* button)
			{
				button->Clicked.AttachLambda([=](GuiGraphicsComposition*, GuiEventArgs&)
				{
					scrollTemplate->GetCommands()->SmallDecrease();
				});
			}

			void GuiCommonScrollBehavior::AttachIncreaseButton(controls::GuiButton* button)
			{
				button->Clicked.AttachLambda([=](GuiGraphicsComposition*, GuiEventArgs&)
				{
					scrollTemplate->GetCommands()->SmallIncrease();
				});
			}

			void GuiCommonScrollBehavior::AttachHorizontalScrollHandle(compositions::GuiPartialViewComposition* partialView)
			{
				partialView->GetParent()->GetEventReceiver()->leftButtonDown.AttachLambda([=](GuiGraphicsComposition*, GuiMouseEventArgs& arguments)
				{
					if (scrollTemplate->GetVisuallyEnabled())
					{
						if (arguments.x < partialView->GetBounds().x1)
						{
							scrollTemplate->GetCommands()->BigDecrease();
						}
						else if (arguments.x >= partialView->GetBounds().x2)
						{
							scrollTemplate->GetCommands()->BigIncrease();
						}
					}
				});

				AttachHorizontalTrackerHandle(partialView);
			}

			void GuiCommonScrollBehavior::AttachVerticalScrollHandle(compositions::GuiPartialViewComposition* partialView)
			{
				partialView->GetParent()->GetEventReceiver()->leftButtonDown.AttachLambda([=](GuiGraphicsComposition*, GuiMouseEventArgs& arguments)
				{
					if (scrollTemplate->GetVisuallyEnabled())
					{
						if (arguments.y < partialView->GetBounds().y1)
						{
							scrollTemplate->GetCommands()->BigDecrease();
						}
						else if (arguments.y >= partialView->GetBounds().y2)
						{
							scrollTemplate->GetCommands()->BigIncrease();
						}
					}
				});

				AttachVerticalTrackerHandle(partialView);
			}

			void GuiCommonScrollBehavior::AttachHorizontalTrackerHandle(compositions::GuiPartialViewComposition* partialView)
			{
				partialView->GetEventReceiver()->mouseMove.AttachLambda([=](GuiGraphicsComposition*, GuiMouseEventArgs& arguments)
				{
					if (dragging)
					{
						auto bounds = partialView->GetParent()->GetBounds();
						vint totalPixels = bounds.x2 - bounds.x1;
						vint currentOffset = partialView->GetBounds().x1;
						vint newOffset = currentOffset + (arguments.x - location.x);
						SetScroll(totalPixels, newOffset);
					}
				});

				AttachHandle(partialView);
			}

			void GuiCommonScrollBehavior::AttachVerticalTrackerHandle(compositions::GuiPartialViewComposition* partialView)
			{
				partialView->GetEventReceiver()->mouseMove.AttachLambda([=](GuiGraphicsComposition*, GuiMouseEventArgs& arguments)
				{
					if (dragging)
					{
						auto bounds = partialView->GetParent()->GetBounds();
						vint totalPixels = bounds.y2 - bounds.y1;
						vint currentOffset = partialView->GetBounds().y1;
						vint newOffset = currentOffset + (arguments.y - location.y);
						SetScroll(totalPixels, newOffset);
					}
				});

				AttachHandle(partialView);
			}

			vint GuiCommonScrollBehavior::GetHorizontalTrackerHandlerPosition(compositions::GuiBoundsComposition* handle, vint totalSize, vint pageSize, vint position)
			{
				vint width = handle->GetParent()->GetBounds().Width() - handle->GetBounds().Width();
				vint max = totalSize - pageSize;
				return max == 0 ? 0 : width * position / max;
			}

			vint GuiCommonScrollBehavior::GetVerticalTrackerHandlerPosition(compositions::GuiBoundsComposition* handle, vint totalSize, vint pageSize, vint position)
			{
				vint height = handle->GetParent()->GetBounds().Height() - handle->GetBounds().Height();
				vint max = totalSize - pageSize;
				return max == 0 ? 0 : height * position / max;
			}
		}
	}
}

/***********************************************************************
.\CONTROLS\TOOLSTRIPPACKAGE\GUIMENUCONTROLS.CPP
***********************************************************************/

namespace vl
{
	namespace presentation
	{
		namespace controls
		{
			using namespace compositions;

/***********************************************************************
IGuiMenuService
***********************************************************************/

			const wchar_t* const IGuiMenuService::Identifier = L"vl::presentation::controls::IGuiMenuService";
			const wchar_t* const IGuiMenuDropdownProvider::Identifier = L"vl::presentation::controls::IGuiMenuDropdownProvider";

			IGuiMenuService::IGuiMenuService()
				:openingMenu(0)
			{
			}

			void IGuiMenuService::MenuItemExecuted()
			{
				if(openingMenu)
				{
					openingMenu->Hide();
				}
				if(GetParentMenuService())
				{
					GetParentMenuService()->MenuItemExecuted();
				}
			}

			GuiMenu* IGuiMenuService::GetOpeningMenu()
			{
				return openingMenu;
			}

			void IGuiMenuService::MenuOpened(GuiMenu* menu)
			{
				if(openingMenu!=menu && openingMenu)
				{
					openingMenu->Hide();
				}
				openingMenu=menu;
			}

			void IGuiMenuService::MenuClosed(GuiMenu* menu)
			{
				if(openingMenu==menu)
				{
					openingMenu=0;
				}
			}

/***********************************************************************
GuiMenu
***********************************************************************/

			void GuiMenu::BeforeControlTemplateUninstalled_()
			{
			}

			void GuiMenu::AfterControlTemplateInstalled_(bool initialize)
			{
			}

			IGuiMenuService* GuiMenu::GetParentMenuService()
			{
				return parentMenuService;
			}

			IGuiMenuService::Direction GuiMenu::GetPreferredDirection()
			{
				return IGuiMenuService::Vertical;
			}

			bool GuiMenu::IsActiveState()
			{
				return true;
			}

			bool GuiMenu::IsSubMenuActivatedByMouseDown()
			{
				return false;
			}

			void GuiMenu::MenuItemExecuted()
			{
				IGuiMenuService::MenuItemExecuted();
				Hide();
			}

			void GuiMenu::OnWindowOpened(compositions::GuiGraphicsComposition* sender, compositions::GuiEventArgs& arguments)
			{
				if(parentMenuService)
				{
					parentMenuService->MenuOpened(this);
				}
			}

			void GuiMenu::OnDeactivatedAltHost()
			{
				Hide();
			}

			void GuiMenu::MouseClickedOnOtherWindow(GuiWindow* window)
			{
				GuiMenu* targetMenu=dynamic_cast<GuiMenu*>(window);
				if(!targetMenu)
				{
					Hide();
				}
			}

			void GuiMenu::OnWindowClosed(compositions::GuiGraphicsComposition* sender, compositions::GuiEventArgs& arguments)
			{
				if(parentMenuService)
				{
					parentMenuService->MenuClosed(this);
					GuiMenu* openingSubMenu=GetOpeningMenu();
					if(openingSubMenu)
					{
						openingSubMenu->Hide();
					}
				}
			}

			GuiMenu::GuiMenu(theme::ThemeName themeName, GuiControl* _owner)
				:GuiPopup(themeName)
				, owner(_owner)
				, parentMenuService(0)
			{
				GetNativeWindow()->SetAlwaysPassFocusToParent(true);
				UpdateMenuService();
				WindowOpened.AttachMethod(this, &GuiMenu::OnWindowOpened);
				WindowClosed.AttachMethod(this, &GuiMenu::OnWindowClosed);
			}

			GuiMenu::~GuiMenu()
			{
			}

			void GuiMenu::UpdateMenuService()
			{
				if(owner)
				{
					parentMenuService=owner->QueryTypedService<IGuiMenuService>();
				}
			}

			IDescriptable* GuiMenu::QueryService(const WString& identifier)
			{
				if(identifier==IGuiMenuService::Identifier)
				{
					return (IGuiMenuService*)this;
				}
				else
				{
					return GuiPopup::QueryService(identifier);
				}
			}

/***********************************************************************
GuiMenuBar
***********************************************************************/

			IGuiMenuService* GuiMenuBar::GetParentMenuService()
			{
				return 0;
			}

			IGuiMenuService::Direction GuiMenuBar::GetPreferredDirection()
			{
				return IGuiMenuService::Horizontal;
			}

			bool GuiMenuBar::IsActiveState()
			{
				return GetOpeningMenu()!=0;
			}

			bool GuiMenuBar::IsSubMenuActivatedByMouseDown()
			{
				return true;
			}

			GuiMenuBar::GuiMenuBar(theme::ThemeName themeName)
				:GuiControl(themeName)
			{
			}

			GuiMenuBar::~GuiMenuBar()
			{
			}

			IDescriptable* GuiMenuBar::QueryService(const WString& identifier)
			{
				if(identifier==IGuiMenuService::Identifier)
				{
					return (IGuiMenuService*)this;
				}
				else
				{
					return GuiControl::QueryService(identifier);
				}
			}

/***********************************************************************
GuiMenuButton
***********************************************************************/

			void GuiMenuButton::BeforeControlTemplateUninstalled_()
			{
				auto host = GetSubMenuHost();
				host->Clicked.Detach(hostClickedHandler);
				host->GetBoundsComposition()->GetEventReceiver()->mouseEnter.Detach(hostMouseEnterHandler);

				hostClickedHandler = nullptr;
				hostMouseEnterHandler = nullptr;
			}

			void GuiMenuButton::AfterControlTemplateInstalled_(bool initialize)
			{
				auto ct = GetControlTemplateObject(true);
				auto host = GetSubMenuHost();

				ct->SetSubMenuOpening(GetSubMenuOpening());
				ct->SetLargeImage(largeImage);
				ct->SetImage(image);
				ct->SetShortcutText(shortcutText);
				ct->SetSubMenuExisting(subMenu != nullptr);

				hostClickedHandler = host->Clicked.AttachMethod(this, &GuiMenuButton::OnClicked);
				hostMouseEnterHandler = host->GetBoundsComposition()->GetEventReceiver()->mouseEnter.AttachMethod(this, &GuiMenuButton::OnMouseEnter);
			}

			GuiButton* GuiMenuButton::GetSubMenuHost()
			{
				GuiButton* button = GetControlTemplateObject(true)->GetSubMenuHost();
				return button ? button : this;
			}

			void GuiMenuButton::OpenSubMenuInternal()
			{
				if(!GetSubMenuOpening())
				{
					if(ownerMenuService)
					{
						GuiMenu* openingSiblingMenu=ownerMenuService->GetOpeningMenu();
						if(openingSiblingMenu)
						{
							openingSiblingMenu->Hide();
						}
					}
					SetSubMenuOpening(true);
				}
			}

			void GuiMenuButton::OnParentLineChanged()
			{
				GuiButton::OnParentLineChanged();
				ownerMenuService=QueryTypedService<IGuiMenuService>();
				if(ownerMenuService)
				{
					SetClickOnMouseUp(!ownerMenuService->IsSubMenuActivatedByMouseDown());
				}
				if(subMenu)
				{
					subMenu->UpdateMenuService();
				}
			}

			bool GuiMenuButton::IsAltAvailable()
			{
				return true;
			}

			compositions::IGuiAltActionHost* GuiMenuButton::GetActivatingAltHost()
			{
				if (subMenu)
				{
					return subMenu->QueryTypedService<IGuiAltActionHost>();
				}
				return 0;
			}

			void GuiMenuButton::OnSubMenuWindowOpened(compositions::GuiGraphicsComposition* sender, compositions::GuiEventArgs& arguments)
			{
				SubMenuOpeningChanged.Execute(GetNotifyEventArguments());
				GetControlTemplateObject(true)->SetSubMenuOpening(true);
			}

			void GuiMenuButton::OnSubMenuWindowClosed(compositions::GuiGraphicsComposition* sender, compositions::GuiEventArgs& arguments)
			{
				SubMenuOpeningChanged.Execute(GetNotifyEventArguments());
				GetControlTemplateObject(true)->SetSubMenuOpening(false);
			}

			void GuiMenuButton::OnMouseEnter(compositions::GuiGraphicsComposition* sender, compositions::GuiEventArgs& arguments)
			{
				if(GetVisuallyEnabled())
				{
					if(cascadeAction && ownerMenuService && ownerMenuService->IsActiveState())
					{
						OpenSubMenuInternal();
					}
				}
			}

			void GuiMenuButton::OnClicked(compositions::GuiGraphicsComposition* sender, compositions::GuiEventArgs& arguments)
			{
				if(GetVisuallyEnabled())
				{
					BeforeSubMenuOpening.Execute(GetNotifyEventArguments());
					if(GetSubMenu())
					{
						OpenSubMenuInternal();
					}
					else if(ownerMenuService)
					{
						ownerMenuService->MenuItemExecuted();
					}
				}
			}

			IGuiMenuService::Direction GuiMenuButton::GetSubMenuDirection()
			{
				return ownerMenuService?ownerMenuService->GetPreferredDirection():IGuiMenuService::Horizontal;
			}

			GuiMenu* GuiMenuButton::ProvideDropdownMenu()
			{
				return GetSubMenu();
			}

			GuiMenuButton::GuiMenuButton(theme::ThemeName themeName)
				:GuiSelectableButton(themeName)
				,subMenu(0)
				,ownedSubMenu(false)
				,ownerMenuService(0)
				,cascadeAction(true)
			{
				SetAutoSelection(false);
				BeforeSubMenuOpening.SetAssociatedComposition(boundsComposition);
				SubMenuOpeningChanged.SetAssociatedComposition(boundsComposition);
				LargeImageChanged.SetAssociatedComposition(boundsComposition);
				ImageChanged.SetAssociatedComposition(boundsComposition);
				ShortcutTextChanged.SetAssociatedComposition(boundsComposition);
			}

			GuiMenuButton::~GuiMenuButton()
			{
				if(subMenu && ownedSubMenu)
				{
					delete subMenu;
				}
			}

			Ptr<GuiImageData> GuiMenuButton::GetLargeImage()
			{
				return largeImage;
			}

			void GuiMenuButton::SetLargeImage(Ptr<GuiImageData> value)
			{
				if (largeImage != value)
				{
					largeImage = value;
					GetControlTemplateObject(true)->SetLargeImage(largeImage);
					LargeImageChanged.Execute(GetNotifyEventArguments());
				}
			}

			Ptr<GuiImageData> GuiMenuButton::GetImage()
			{
				return image;
			}

			void GuiMenuButton::SetImage(Ptr<GuiImageData> value)
			{
				if (image != value)
				{
					image = value;
					GetControlTemplateObject(true)->SetImage(image);
					ImageChanged.Execute(GetNotifyEventArguments());
				}
			}

			const WString& GuiMenuButton::GetShortcutText()
			{
				return shortcutText;
			}

			void GuiMenuButton::SetShortcutText(const WString& value)
			{
				if (shortcutText != value)
				{
					shortcutText = value;
					GetControlTemplateObject(true)->SetShortcutText(shortcutText);
					ShortcutTextChanged.Execute(GetNotifyEventArguments());
				}
			}

			bool GuiMenuButton::IsSubMenuExists()
			{
				return subMenu!=0;
			}

			GuiMenu* GuiMenuButton::GetSubMenu()
			{
				return subMenu;
			}

			GuiMenu* GuiMenuButton::CreateSubMenu(TemplateProperty<templates::GuiMenuTemplate> subMenuTemplate)
			{
				if (!subMenu)
				{
					GuiMenu* newSubMenu = new GuiMenu(theme::ThemeName::Menu, this);
					newSubMenu->SetControlTemplate(subMenuTemplate ? subMenuTemplate : GetControlTemplateObject(true)->GetSubMenuTemplate());
					SetSubMenu(newSubMenu, true);
				}
				return subMenu;
			}

			void GuiMenuButton::SetSubMenu(GuiMenu* value, bool owned)
			{
				if(subMenu)
				{
					if(ownedSubMenu)
					{
						delete subMenu;
					}
				}
				subMenu=value;
				ownedSubMenu=owned;
				if(subMenu)
				{
					subMenu->WindowOpened.AttachMethod(this, &GuiMenuButton::OnSubMenuWindowOpened);
					subMenu->WindowClosed.AttachMethod(this, &GuiMenuButton::OnSubMenuWindowClosed);
				}
				GetControlTemplateObject(true)->SetSubMenuExisting(subMenu != nullptr);
			}

			void GuiMenuButton::DestroySubMenu()
			{
				if(subMenu)
				{
					if(ownedSubMenu)
					{
						delete subMenu;
					}
					subMenu=0;
					ownedSubMenu=false;
					GetControlTemplateObject(true)->SetSubMenuExisting(false);
				}
			}

			bool GuiMenuButton::GetOwnedSubMenu()
			{
				return subMenu && ownedSubMenu;
			}

			bool GuiMenuButton::GetSubMenuOpening()
			{
				if(subMenu)
				{
					return subMenu->GetOpening();
				}
				else
				{
					return false;
				}
			}

			void GuiMenuButton::SetSubMenuOpening(bool value)
			{
				if(subMenu)
				{
					if(value)
					{
						subMenu->SetClientSize(preferredMenuClientSize);
						IGuiMenuService::Direction direction=GetSubMenuDirection();
						subMenu->ShowPopup(GetSubMenuHost(), direction==IGuiMenuService::Horizontal);
					}
					else
					{
						subMenu->Close();
					}
				}
			}

			Size GuiMenuButton::GetPreferredMenuClientSize()
			{
				return preferredMenuClientSize;
			}

			void GuiMenuButton::SetPreferredMenuClientSize(Size value)
			{
				preferredMenuClientSize=value;
			}

			bool GuiMenuButton::GetCascadeAction()
			{
				return cascadeAction;
			}

			void GuiMenuButton::SetCascadeAction(bool value)
			{
				cascadeAction=value;
			}

			IDescriptable* GuiMenuButton::QueryService(const WString& identifier)
			{
				if (identifier == IGuiMenuDropdownProvider::Identifier)
				{
					return (IGuiMenuDropdownProvider*)this;
				}
				else
				{
					return GuiSelectableButton::QueryService(identifier);
				}
			}
		}
	}
}

/***********************************************************************
.\CONTROLS\GUIDIALOGS.CPP
***********************************************************************/

namespace vl
{
	namespace presentation
	{
		namespace controls
		{
			using namespace elements;
			using namespace compositions;
			using namespace collections;
			using namespace reflection::description;

/***********************************************************************
GuiDialogBase
***********************************************************************/

			GuiWindow* GuiDialogBase::GetHostWindow()
			{
				if (rootObject)
				{
					if (auto control = dynamic_cast<GuiControl*>(rootObject))
					{
						if (auto host = control->GetRelatedControlHost())
						{
							return dynamic_cast<GuiWindow*>(host);
						}
					}
					else if (auto composition = dynamic_cast<GuiGraphicsComposition*>(rootObject))
					{
						if (auto host = composition->GetRelatedControlHost())
						{
							return dynamic_cast<GuiWindow*>(host);
						}
					}
				}
				return nullptr;
			}

			GuiDialogBase::GuiDialogBase()
			{
			}

			GuiDialogBase::~GuiDialogBase()
			{
			}

			void GuiDialogBase::Attach(GuiInstanceRootObject* _rootObject)
			{
				rootObject = _rootObject;
			}

			void GuiDialogBase::Detach(GuiInstanceRootObject* _rootObject)
			{
				rootObject = nullptr;
			}

/***********************************************************************
GuiMessageDialog
***********************************************************************/

			GuiMessageDialog::GuiMessageDialog()
			{
			}

			GuiMessageDialog::~GuiMessageDialog()
			{
			}

			INativeDialogService::MessageBoxButtonsInput GuiMessageDialog::GetInput()
			{
				return input;
			}

			void GuiMessageDialog::SetInput(INativeDialogService::MessageBoxButtonsInput value)
			{
				input = value;
			}

			INativeDialogService::MessageBoxDefaultButton GuiMessageDialog::GetDefaultButton()
			{
				return defaultButton;
			}

			void GuiMessageDialog::SetDefaultButton(INativeDialogService::MessageBoxDefaultButton value)
			{
				defaultButton = value;
			}

			INativeDialogService::MessageBoxIcons GuiMessageDialog::GetIcon()
			{
				return icon;
			}

			void GuiMessageDialog::SetIcon(INativeDialogService::MessageBoxIcons value)
			{
				icon = value;
			}

			INativeDialogService::MessageBoxModalOptions GuiMessageDialog::GetModalOption()
			{
				return modalOption;
			}

			void GuiMessageDialog::SetModalOption(INativeDialogService::MessageBoxModalOptions value)
			{
				modalOption = value;
			}

			const WString& GuiMessageDialog::GetText()
			{
				return text;
			}

			void GuiMessageDialog::SetText(const WString& value)
			{
				text = value;
			}

			const WString& GuiMessageDialog::GetTitle()
			{
				return title;
			}

			void GuiMessageDialog::SetTitle(const WString& value)
			{
				title = value;
			}

			INativeDialogService::MessageBoxButtonsOutput GuiMessageDialog::ShowDialog()
			{
				auto service = GetCurrentController()->DialogService();
				return service->ShowMessageBox(GetHostWindow()->GetNativeWindow(), text, title, input, defaultButton, icon, modalOption);
			}

/***********************************************************************
GuiColorDialog
***********************************************************************/

			GuiColorDialog::GuiColorDialog()
			{
				for (vint i = 0; i < 16; i++)
				{
					customColors.Add(Color());
				}
			}

			GuiColorDialog::~GuiColorDialog()
			{
			}

			bool GuiColorDialog::GetEnabledCustomColor()
			{
				return enabledCustomColor;
			}

			void GuiColorDialog::SetEnabledCustomColor(bool value)
			{
				enabledCustomColor = value;
			}

			bool GuiColorDialog::GetOpenedCustomColor()
			{
				return openedCustomColor;
			}

			void GuiColorDialog::SetOpenedCustomColor(bool value)
			{
				openedCustomColor = value;
			}

			Color GuiColorDialog::GetSelectedColor()
			{
				return selectedColor;
			}

			void GuiColorDialog::SetSelectedColor(Color value)
			{
				if (selectedColor != value)
				{
					selectedColor = value;
					SelectedColorChanged.Execute(GuiEventArgs());
				}
			}

			collections::List<Color>& GuiColorDialog::GetCustomColors()
			{
				return customColors;
			}

			bool GuiColorDialog::ShowDialog()
			{
				Array<Color> colors;
				CopyFrom(colors, customColors);
				colors.Resize(16);

				INativeDialogService::ColorDialogCustomColorOptions options =
					!enabledCustomColor ? INativeDialogService::CustomColorDisabled :
					!openedCustomColor ? INativeDialogService::CustomColorEnabled :
					INativeDialogService::CustomColorOpened;

				auto service = GetCurrentController()->DialogService();
				if (!service->ShowColorDialog(GetHostWindow()->GetNativeWindow(), selectedColor, showSelection, options, &colors[0]))
				{
					return false;
				}

				CopyFrom(customColors, colors);
				SelectedColorChanged.Execute(GuiEventArgs());
				return true;
			}

/***********************************************************************
GuiFontDialog
***********************************************************************/

			GuiFontDialog::GuiFontDialog()
			{
			}

			GuiFontDialog::~GuiFontDialog()
			{
			}

			const FontProperties& GuiFontDialog::GetSelectedFont()
			{
				return selectedFont;
			}

			void GuiFontDialog::SetSelectedFont(const FontProperties& value)
			{
				if (selectedFont != value)
				{
					selectedFont = value;
					SelectedFontChanged.Execute(GuiEventArgs());
				}
			}

			Color GuiFontDialog::GetSelectedColor()
			{
				return selectedColor;
			}

			void GuiFontDialog::SetSelectedColor(Color value)
			{
				if (selectedColor != value)
				{
					selectedColor = value;
					SelectedColorChanged.Execute(GuiEventArgs());
				}
			}

			bool GuiFontDialog::GetShowSelection()
			{
				return showSelection;
			}

			void GuiFontDialog::SetShowSelection(bool value)
			{
				showSelection = value;
			}

			bool GuiFontDialog::GetShowEffect()
			{
				return showEffect;
			}

			void GuiFontDialog::SetShowEffect(bool value)
			{
				showEffect = value;
			}

			bool GuiFontDialog::GetForceFontExist()
			{
				return forceFontExist;
			}

			void GuiFontDialog::SetForceFontExist(bool value)
			{
				forceFontExist = value;
			}

			bool GuiFontDialog::ShowDialog()
			{
				auto service = GetCurrentController()->DialogService();
				if (!service->ShowFontDialog(GetHostWindow()->GetNativeWindow(), selectedFont, selectedColor, showSelection, showEffect, forceFontExist))
				{
					return false;
				}

				SelectedColorChanged.Execute(GuiEventArgs());
				SelectedFontChanged.Execute(GuiEventArgs());
				return true;
			}

/***********************************************************************
GuiFileDialogBase
***********************************************************************/

			GuiFileDialogBase::GuiFileDialogBase()
			{
			}

			GuiFileDialogBase::~GuiFileDialogBase()
			{
			}

			const WString& GuiFileDialogBase::GetFilter()
			{
				return filter;
			}

			void GuiFileDialogBase::SetFilter(const WString& value)
			{
				filter = value;
			}

			vint GuiFileDialogBase::GetFilterIndex()
			{
				return filterIndex;
			}

			void GuiFileDialogBase::SetFilterIndex(vint value)
			{
				if (filterIndex != value)
				{
					filterIndex = value;
					FilterIndexChanged.Execute(GuiEventArgs());
				}
			}

			bool GuiFileDialogBase::GetEnabledPreview()
			{
				return enabledPreview;
			}

			void GuiFileDialogBase::SetEnabledPreview(bool value)
			{
				enabledPreview = value;
			}

			WString GuiFileDialogBase::GetTitle()
			{
				return title;
			}

			void GuiFileDialogBase::SetTitle(const WString& value)
			{
				title = value;
			}

			WString GuiFileDialogBase::GetFileName()
			{
				return fileName;
			}

			void GuiFileDialogBase::SetFileName(const WString& value)				
			{
				if (fileName != value)
				{
					FileNameChanged.Execute(GuiEventArgs());
				}
			}

			WString GuiFileDialogBase::GetDirectory()
			{
				return directory;
			}

			void GuiFileDialogBase::SetDirectory(const WString& value)
			{
				directory = value;
			}

			WString GuiFileDialogBase::GetDefaultExtension()
			{
				return defaultExtension;
			}

			void GuiFileDialogBase::SetDefaultExtension(const WString& value)
			{
				defaultExtension = value;
			}

			INativeDialogService::FileDialogOptions GuiFileDialogBase::GetOptions()
			{
				return options;
			}

			void GuiFileDialogBase::SetOptions(INativeDialogService::FileDialogOptions value)
			{
				options = value;
			}

/***********************************************************************
GuiOpenFileDialog
***********************************************************************/

			GuiOpenFileDialog::GuiOpenFileDialog()
			{
			}

			GuiOpenFileDialog::~GuiOpenFileDialog()
			{
			}

			collections::List<WString>& GuiOpenFileDialog::GetFileNames()
			{
				return fileNames;
			}

			bool GuiOpenFileDialog::ShowDialog()
			{
				fileNames.Clear();
				auto service = GetCurrentController()->DialogService();
				if (!service->ShowFileDialog(
					GetHostWindow()->GetNativeWindow(),
					fileNames,
					filterIndex,
					(enabledPreview ? INativeDialogService::FileDialogOpenPreview : INativeDialogService::FileDialogOpen),
					title,
					fileName,
					directory,
					defaultExtension,
					filter,
					options))
				{
					return false;
				}

				if (fileNames.Count() > 0)
				{
					fileName = fileNames[0];
					FileNameChanged.Execute(GuiEventArgs());
					FilterIndexChanged.Execute(GuiEventArgs());
				}
				return true;
			}

/***********************************************************************
GuiSaveFileDialog
***********************************************************************/

			GuiSaveFileDialog::GuiSaveFileDialog()
			{
			}

			GuiSaveFileDialog::~GuiSaveFileDialog()
			{
			}

			bool GuiSaveFileDialog::ShowDialog()
			{
				List<WString> fileNames;
				auto service = GetCurrentController()->DialogService();
				if (!service->ShowFileDialog(
					GetHostWindow()->GetNativeWindow(),
					fileNames,
					filterIndex,
					(enabledPreview ? INativeDialogService::FileDialogSavePreview : INativeDialogService::FileDialogSave),
					title,
					fileName,
					directory,
					defaultExtension,
					filter,
					options))
				{
					return false;
				}

				if (fileNames.Count() > 0)
				{
					fileName = fileNames[0];
					FileNameChanged.Execute(GuiEventArgs());
					FilterIndexChanged.Execute(GuiEventArgs());
				}
				return true;
			}
		}
	}
}

/***********************************************************************
.\CONTROLS\LISTCONTROLPACKAGE\GUIBINDABLEDATAGRID.CPP
***********************************************************************/

namespace vl
{
	namespace presentation
	{
		namespace controls
		{
			using namespace collections;
			using namespace description;
			using namespace templates;

			namespace list
			{

/***********************************************************************
DataFilterBase
***********************************************************************/

				void DataFilterBase::InvokeOnProcessorChanged()
				{
					if (callback)
					{
						callback->OnProcessorChanged();
					}
				}

				DataFilterBase::DataFilterBase()
				{
				}

				void DataFilterBase::SetCallback(IDataProcessorCallback* value)
				{
					callback = value;
				}

/***********************************************************************
DataMultipleFilter
***********************************************************************/

				DataMultipleFilter::DataMultipleFilter()
				{
				}

				bool DataMultipleFilter::AddSubFilter(Ptr<IDataFilter> value)
				{
					if (!value) return false;
					if (filters.Contains(value.Obj())) return false;
					filters.Add(value);
					value->SetCallback(callback);
					InvokeOnProcessorChanged();
					return true;
				}

				bool DataMultipleFilter::RemoveSubFilter(Ptr<IDataFilter> value)
				{
					if (!value) return false;
					if (!filters.Contains(value.Obj())) return false;
					value->SetCallback(nullptr);
					filters.Remove(value.Obj());
					InvokeOnProcessorChanged();
					return true;
				}

				void DataMultipleFilter::SetCallback(IDataProcessorCallback* value)
				{
					DataFilterBase::SetCallback(value);
					for (vint i = 0; i < filters.Count(); i++)
					{
						filters[i]->SetCallback(value);
					}
				}

/***********************************************************************
DataAndFilter
***********************************************************************/

				DataAndFilter::DataAndFilter()
				{
				}

				bool DataAndFilter::Filter(const description::Value& row)
				{
					return From(filters)
						.All([row](Ptr<IDataFilter> filter)
					{
						return filter->Filter(row);
					});
				}

/***********************************************************************
DataOrFilter
***********************************************************************/

				DataOrFilter::DataOrFilter()
				{
				}

				bool DataOrFilter::Filter(const description::Value& row)
				{
					return From(filters)
						.Any([row](Ptr<IDataFilter> filter)
					{
						return filter->Filter(row);
					});
				}

/***********************************************************************
DataNotFilter
***********************************************************************/

				DataNotFilter::DataNotFilter()
				{
				}

				bool DataNotFilter::SetSubFilter(Ptr<IDataFilter> value)
				{
					if (filter == value) return false;
					if (filter) filter->SetCallback(nullptr);
					filter = value;
					if (filter) filter->SetCallback(callback);
					InvokeOnProcessorChanged();
					return true;
				}

				void DataNotFilter::SetCallback(IDataProcessorCallback* value)
				{
					DataFilterBase::SetCallback(value);
					if (filter) filter->SetCallback(value);
				}

				bool DataNotFilter::Filter(const description::Value& row)
				{
					return filter ? true : !filter->Filter(row);
				}

/***********************************************************************
DataSorterBase
***********************************************************************/

				void DataSorterBase::InvokeOnProcessorChanged()
				{
					if (callback)
					{
						callback->OnProcessorChanged();
					}
				}

				DataSorterBase::DataSorterBase()
				{
				}

				void DataSorterBase::SetCallback(IDataProcessorCallback* value)
				{
					callback = value;
				}

/***********************************************************************
DataMultipleSorter
***********************************************************************/

				DataMultipleSorter::DataMultipleSorter()
				{
				}

				bool DataMultipleSorter::SetLeftSorter(Ptr<IDataSorter> value)
				{
					if (leftSorter == value) return false;
					if (leftSorter) leftSorter->SetCallback(nullptr);
					leftSorter = value;
					if (leftSorter) leftSorter->SetCallback(callback);
					return true;
				}

				bool DataMultipleSorter::SetRightSorter(Ptr<IDataSorter> value)
				{
					if (rightSorter == value) return false;
					if (rightSorter) rightSorter->SetCallback(nullptr);
					rightSorter = value;
					if (rightSorter) rightSorter->SetCallback(callback);
					return true;
				}

				void DataMultipleSorter::SetCallback(IDataProcessorCallback* value)
				{
					DataSorterBase::SetCallback(value);
					if (leftSorter) leftSorter->SetCallback(value);
					if (rightSorter) rightSorter->SetCallback(value);
				}

				vint DataMultipleSorter::Compare(const description::Value& row1, const description::Value& row2)
				{
					if (leftSorter)
					{
						vint result = leftSorter->Compare(row1, row2);
						if (result != 0) return result;
					}
					if (rightSorter)
					{
						vint result = rightSorter->Compare(row1, row2);
						if (result != 0) return result;
					}
					return 0;
				}

/***********************************************************************
DataReverseSorter
***********************************************************************/

				DataReverseSorter::DataReverseSorter()
				{
				}

				bool DataReverseSorter::SetSubSorter(Ptr<IDataSorter> value)
				{
					if (sorter == value) return false;
					if (sorter) sorter->SetCallback(nullptr);
					sorter = value;
					if (sorter) sorter->SetCallback(callback);
					return true;
				}

				void DataReverseSorter::SetCallback(IDataProcessorCallback* value)
				{
					DataSorterBase::SetCallback(value);
					if (sorter) sorter->SetCallback(value);
				}

				vint DataReverseSorter::Compare(const description::Value& row1, const description::Value& row2)
				{
					return sorter ? -sorter->Compare(row1, row2) : 0;
				}

/***********************************************************************
DataColumn
***********************************************************************/

				void DataColumn::NotifyAllColumnsUpdate(bool affectItem)
				{
					if (dataProvider)
					{
						vint index = dataProvider->columns.IndexOf(this);
						if (index != -1)
						{
							dataProvider->columns.NotifyColumnUpdated(index, affectItem);
						}
					}
				}

				DataColumn::DataColumn()
				{
				}

				DataColumn::~DataColumn()
				{
					if (popup && ownPopup)
					{
						SafeDeleteControl(popup);
					}
				}

				WString DataColumn::GetText()
				{
					return text;
				}

				void DataColumn::SetText(const WString& value)
				{
					if (text != value)
					{
						text = value;
						NotifyAllColumnsUpdate(false);
					}
				}

				vint DataColumn::GetSize()
				{
					return size;
				}

				void DataColumn::SetSize(vint value)
				{
					if (size != value)
					{
						size = value;
						NotifyAllColumnsUpdate(false);
					}
				}

				bool DataColumn::GetOwnPopup()
				{
					return ownPopup;
				}

				void DataColumn::SetOwnPopup(bool value)
				{
					ownPopup = value;
				}

				GuiMenu* DataColumn::GetPopup()
				{
					return popup;
				}

				void DataColumn::SetPopup(GuiMenu* value)
				{
					if (popup != value)
					{
						popup = value;
						NotifyAllColumnsUpdate(false);
					}
				}

				Ptr<IDataFilter> DataColumn::GetFilter()
				{
					return associatedFilter;
				}

				void DataColumn::SetFilter(Ptr<IDataFilter> value)
				{
					if (associatedFilter) associatedFilter->SetCallback(nullptr);
					associatedFilter = value;
					if (associatedFilter) associatedFilter->SetCallback(dataProvider);
					NotifyAllColumnsUpdate(false);
				}

				Ptr<IDataSorter> DataColumn::GetSorter()
				{
					return associatedSorter;
				}

				void DataColumn::SetSorter(Ptr<IDataSorter> value)
				{
					if (associatedSorter) associatedSorter->SetCallback(nullptr);
					associatedSorter = value;
					if (associatedSorter) associatedSorter->SetCallback(dataProvider);
					NotifyAllColumnsUpdate(false);
				}

				Ptr<IDataVisualizerFactory> DataColumn::GetVisualizerFactory()
				{
					return visualizerFactory;
				}

				void DataColumn::SetVisualizerFactory(Ptr<IDataVisualizerFactory> value)
				{
					visualizerFactory = value;
					NotifyAllColumnsUpdate(true);
				}

				Ptr<IDataEditorFactory> DataColumn::GetEditorFactory()
				{
					return editorFactory;
				}

				void DataColumn::SetEditorFactory(Ptr<IDataEditorFactory> value)
				{
					editorFactory = value;
					NotifyAllColumnsUpdate(true);
				}

				WString DataColumn::GetCellText(vint row)
				{
					if (0 <= row && row < dataProvider->Count())
					{
						return ReadProperty(dataProvider->GetBindingValue(row), textProperty);
					}
					return L"";
				}

				description::Value DataColumn::GetCellValue(vint row)
				{
					if (0 <= row && row < dataProvider->Count())
					{
						return ReadProperty(dataProvider->GetBindingValue(row), valueProperty);
					}
					return Value();
				}

				void DataColumn::SetCellValue(vint row, description::Value value)
				{
					if (0 <= row && row < dataProvider->Count())
					{
						auto rowValue = dataProvider->GetBindingValue(row);
						WriteProperty(rowValue, valueProperty, value);
						dataProvider->InvokeOnItemModified(row, 1, 1);
					}
				}

				ItemProperty<WString> DataColumn::GetTextProperty()
				{
					return textProperty;
				}

				void DataColumn::SetTextProperty(const ItemProperty<WString>& value)
				{
					if (textProperty != value)
					{
						textProperty = value;
						NotifyAllColumnsUpdate(true);
						compositions::GuiEventArgs arguments;
						TextPropertyChanged.Execute(arguments);
					}
				}

				WritableItemProperty<description::Value> DataColumn::GetValueProperty()
				{
					return valueProperty;
				}

				void DataColumn::SetValueProperty(const WritableItemProperty<description::Value>& value)
				{
					if (valueProperty != value)
					{
						valueProperty = value;
						NotifyAllColumnsUpdate(true);
						compositions::GuiEventArgs arguments;
						ValuePropertyChanged.Execute(arguments);
					}
				}

/***********************************************************************
DataColumns
***********************************************************************/

				void DataColumns::NotifyColumnUpdated(vint index, bool affectItem)
				{
					affectItemFlag = affectItem;
					NotifyUpdateInternal(index, 1, 1);
					affectItemFlag = true;
				}

				void DataColumns::NotifyUpdateInternal(vint start, vint count, vint newCount)
				{
					dataProvider->NotifyAllColumnsUpdate();
					if (affectItemFlag)
					{
						dataProvider->NotifyAllItemsUpdate();
					}
				}

				bool DataColumns::QueryInsert(vint index, const Ptr<DataColumn>& value)
				{
					return !items.Contains(value.Obj());
				}

				void DataColumns::AfterInsert(vint index, const Ptr<DataColumn>& value)
				{
					value->dataProvider = dataProvider;
				}

				void DataColumns::BeforeRemove(vint index, const Ptr<DataColumn>& value)
				{
					value->dataProvider = nullptr;
				}

				DataColumns::DataColumns(DataProvider* _dataProvider)
					:dataProvider(_dataProvider)
				{
				}

				DataColumns::~DataColumns()
				{
				}

/***********************************************************************
DataProvider
***********************************************************************/

				void DataProvider::NotifyAllItemsUpdate()
				{
					InvokeOnItemModified(0, Count(), Count());
				}

				void DataProvider::NotifyAllColumnsUpdate()
				{
					if (columnItemViewCallback)
					{
						columnItemViewCallback->OnColumnChanged();
					}
				}

				GuiListControl::IItemProvider* DataProvider::GetItemProvider()
				{
					return this;
				}

				void DataProvider::OnProcessorChanged()
				{
					RebuildFilter();
					ReorderRows(true);
				}

				void DataProvider::OnItemSourceModified(vint start, vint count, vint newCount)
				{
					if (!currentSorter && !currentFilter && count == newCount)
					{
						InvokeOnItemModified(start, count, newCount);
					}
					else
					{
						ReorderRows(true);
					}
				}

				ListViewDataColumns& DataProvider::GetDataColumns()
				{
					return dataColumns;
				}

				DataColumns& DataProvider::GetColumns()
				{
					return columns;
				}

				Ptr<description::IValueEnumerable> DataProvider::GetItemSource()
				{
					return itemSource;
				}

				void DataProvider::SetItemSource(Ptr<description::IValueEnumerable> _itemSource)
				{
					vint oldCount = 0;
					if (itemSource)
					{
						oldCount = itemSource->GetCount();
					}
					if (itemChangedEventHandler)
					{
						auto ol = itemSource.Cast<IValueObservableList>();
						ol->ItemChanged.Remove(itemChangedEventHandler);
					}

					itemSource = nullptr;
					itemChangedEventHandler = nullptr;

					if (_itemSource)
					{
						if (auto ol = _itemSource.Cast<IValueObservableList>())
						{
							itemSource = ol;
							itemChangedEventHandler = ol->ItemChanged.Add([this](vint start, vint oldCount, vint newCount)
							{
								OnItemSourceModified(start, oldCount, newCount);
							});
						}
						else if (auto rl = _itemSource.Cast<IValueReadonlyList>())
						{
							itemSource = rl;
						}
						else
						{
							itemSource = IValueList::Create(GetLazyList<Value>(_itemSource));
						}
					}

					OnItemSourceModified(0, oldCount, itemSource ? itemSource->GetCount() : 0);
				}

				void DataProvider::RebuildFilter()
				{
					if (currentFilter)
					{
						currentFilter->SetCallback(nullptr);
						currentFilter = nullptr;
					}

					List<Ptr<IDataFilter>> selectedFilters;
					CopyFrom(
						selectedFilters,
						From(columns)
						.Select([](Ptr<DataColumn> column) {return column->GetFilter(); })
						.Where([](Ptr<IDataFilter> filter) {return filter != nullptr; })
					);
					if (additionalFilter)
					{
						selectedFilters.Add(additionalFilter);
					}
					if (selectedFilters.Count() > 0)
					{
						auto andFilter = MakePtr<DataAndFilter>();
						FOREACH(Ptr<IDataFilter>, filter, selectedFilters)
						{
							andFilter->AddSubFilter(filter);
						}
						currentFilter = andFilter;
					}

					if (currentFilter)
					{
						currentFilter->SetCallback(this);
					}
				}

				void DataProvider::ReorderRows(bool invokeCallback)
				{
					vint oldRowCount = virtualRowToSourceRow.Count();
					virtualRowToSourceRow.Clear();
					vint rowCount = itemSource ? itemSource->GetCount() : 0;

					if (currentFilter)
					{
						for (vint i = 0; i < rowCount; i++)
						{
							if (currentFilter->Filter(itemSource->Get(i)))
							{
								virtualRowToSourceRow.Add(i);
							}
						}
					}
					else
					{
						for (vint i = 0; i < rowCount; i++)
						{
							virtualRowToSourceRow.Add(i);
						}
					}

					if (currentSorter && virtualRowToSourceRow.Count() > 0)
					{
						IDataSorter* sorter = currentSorter.Obj();
						SortLambda(
							&virtualRowToSourceRow[0],
							virtualRowToSourceRow.Count(),
							[=](vint a, vint b)
							{
								return sorter->Compare(itemSource->Get(a), itemSource->Get(b));
							});
					}

					if (invokeCallback)
					{
						NotifyAllItemsUpdate();
					}
				}

				DataProvider::DataProvider()
					:dataColumns(this)
					, columns(this)
				{
					RebuildFilter();
					ReorderRows(false);
				}

				DataProvider::~DataProvider()
				{
				}

				Ptr<IDataFilter> DataProvider::GetAdditionalFilter()
				{
					return additionalFilter;
				}

				void DataProvider::SetAdditionalFilter(Ptr<IDataFilter> value)
				{
					additionalFilter = value;
					RebuildFilter();
					ReorderRows(true);
				}

				// ===================== GuiListControl::IItemProvider =====================

				vint DataProvider::Count()
				{
					return virtualRowToSourceRow.Count();
				}

				WString DataProvider::GetTextValue(vint itemIndex)
				{
					return GetText(itemIndex);
				}

				description::Value DataProvider::GetBindingValue(vint itemIndex)
				{
					return itemSource ? itemSource->Get(virtualRowToSourceRow[itemIndex]) : Value();
				}

				IDescriptable* DataProvider::RequestView(const WString& identifier)
				{
					if (identifier == IListViewItemView::Identifier)
					{
						return (IListViewItemView*)this;
					}
					else if (identifier == ListViewColumnItemArranger::IColumnItemView::Identifier)
					{
						return (ListViewColumnItemArranger::IColumnItemView*)this;
					}
					else if (identifier == IDataGridView::Identifier)
					{
						return (IDataGridView*)this;
					}
					else
					{
						return nullptr;
					}
				}

				// ===================== list::IListViewItemProvider =====================

				Ptr<GuiImageData> DataProvider::GetSmallImage(vint itemIndex)
				{
					if (0 <= itemIndex && itemIndex < Count())
					{
						return ReadProperty(GetBindingValue(itemIndex), smallImageProperty);
					}
					return nullptr;
				}

				Ptr<GuiImageData> DataProvider::GetLargeImage(vint itemIndex)
				{
					if (0 <= itemIndex && itemIndex < Count())
					{
						return ReadProperty(GetBindingValue(itemIndex), largeImageProperty);
					}
					return nullptr;
				}

				WString DataProvider::GetText(vint itemIndex)
				{
					if (columns.Count() == 0)return L"";
					return columns[0]->GetCellText(itemIndex);
				}

				WString DataProvider::GetSubItem(vint itemIndex, vint index)
				{
					return columns[index + 1]->GetCellText(itemIndex);
				}

				vint DataProvider::GetDataColumnCount()
				{
					return dataColumns.Count();
				}

				vint DataProvider::GetDataColumn(vint index)
				{
					return dataColumns[index];
				}

				vint DataProvider::GetColumnCount()
				{
					return columns.Count();
				}

				WString DataProvider::GetColumnText(vint index)
				{
					return columns[index]->GetText();
				}

				// ===================== list::ListViewColumnItemArranger::IColumnItemView =====================

				bool DataProvider::AttachCallback(ListViewColumnItemArranger::IColumnItemViewCallback* value)
				{
					if (columnItemViewCallback)return false;
					columnItemViewCallback = value;
					return true;
				}

				bool DataProvider::DetachCallback(ListViewColumnItemArranger::IColumnItemViewCallback* value)
				{
					if (!columnItemViewCallback) return false;
					columnItemViewCallback = nullptr;
					return true;
				}

				vint DataProvider::GetColumnSize(vint index)
				{
					return columns[index]->GetSize();
				}

				void DataProvider::SetColumnSize(vint index, vint value)
				{
					columns[index]->SetSize(value);
				}

				GuiMenu* DataProvider::GetDropdownPopup(vint index)
				{
					return columns[index]->GetPopup();
				}

				ColumnSortingState DataProvider::GetSortingState(vint index)
				{
					return columns[index]->sortingState;
				}

				// ===================== list::IDataGridView =====================

				bool DataProvider::IsColumnSortable(vint column)
				{
					return columns[column]->GetSorter();
				}

				void DataProvider::SortByColumn(vint column, bool ascending)
				{
					if (0 <= column && column < columns.Count())
					{
						auto sorter = columns[column]->GetSorter();
						if (!sorter)
						{
							currentSorter = nullptr;
						}
						else if (ascending)
						{
							currentSorter = sorter;
						}
						else
						{
							Ptr<DataReverseSorter> reverseSorter = new DataReverseSorter();
							reverseSorter->SetSubSorter(sorter);
							currentSorter = reverseSorter;
						}
					}
					else
					{
						currentSorter = nullptr;
					}

					for (vint i = 0; i < columns.Count(); i++)
					{
						columns[i]->sortingState =
							i != column ? ColumnSortingState::NotSorted :
							ascending ? ColumnSortingState::Ascending :
							ColumnSortingState::Descending
							;
					}
					NotifyAllColumnsUpdate();
					ReorderRows(true);
				}

				vint DataProvider::GetSortedColumn()
				{
					for (vint i = 0; i < columns.Count(); i++)
					{
						auto state = columns[i]->sortingState;
						if (state != ColumnSortingState::NotSorted)
						{
							return i;
						}
					}
					return -1;
				}

				bool DataProvider::IsSortOrderAscending()
				{
					for (vint i = 0; i < columns.Count(); i++)
					{
						auto state = columns[i]->sortingState;
						if (state != ColumnSortingState::NotSorted)
						{
							return state == ColumnSortingState::Ascending;
						}
					}
					return true;
				}

				vint DataProvider::GetCellSpan(vint row, vint column)
				{
					return 1;
				}

				IDataVisualizerFactory* DataProvider::GetCellDataVisualizerFactory(vint row, vint column)
				{
					return columns[column]->GetVisualizerFactory().Obj();
				}

				IDataEditorFactory* DataProvider::GetCellDataEditorFactory(vint row, vint column)
				{
					return columns[column]->GetEditorFactory().Obj();
				}

				description::Value DataProvider::GetBindingCellValue(vint row, vint column)
				{
					return columns[column]->GetCellValue(row);
				}

				void DataProvider::SetBindingCellValue(vint row, vint column, const description::Value& value)
				{
					columns[column]->SetCellValue(row, value);
				}
			}

/***********************************************************************
GuiBindableDataGrid
***********************************************************************/

			GuiBindableDataGrid::GuiBindableDataGrid(theme::ThemeName themeName)
				:GuiVirtualDataGrid(themeName, new list::DataProvider)
			{
				dataProvider = dynamic_cast<list::DataProvider*>(GetItemProvider());
			}

			GuiBindableDataGrid::~GuiBindableDataGrid()
			{
			}
			
			list::ListViewDataColumns& GuiBindableDataGrid::GetDataColumns()
			{
				return dataProvider->GetDataColumns();
			}

			list::DataColumns& GuiBindableDataGrid::GetColumns()
			{
				return dataProvider->GetColumns();
			}

			Ptr<description::IValueEnumerable> GuiBindableDataGrid::GetItemSource()
			{
				return dataProvider->GetItemSource();
			}

			void GuiBindableDataGrid::SetItemSource(Ptr<description::IValueEnumerable> _itemSource)
			{
				dataProvider->SetItemSource(_itemSource);
			}

			Ptr<list::IDataFilter> GuiBindableDataGrid::GetAdditionalFilter()
			{
				return dataProvider->GetAdditionalFilter();
			}

			void GuiBindableDataGrid::SetAdditionalFilter(Ptr<list::IDataFilter> value)
			{
				dataProvider->SetAdditionalFilter(value);
			}

			ItemProperty<Ptr<GuiImageData>> GuiBindableDataGrid::GetLargeImageProperty()
			{
				return dataProvider->largeImageProperty;
			}

			void GuiBindableDataGrid::SetLargeImageProperty(const ItemProperty<Ptr<GuiImageData>>& value)
			{
				if (dataProvider->largeImageProperty != value)
				{
					dataProvider->largeImageProperty = value;
					dataProvider->NotifyAllItemsUpdate();
					LargeImagePropertyChanged.Execute(GetNotifyEventArguments());
				}
			}

			ItemProperty<Ptr<GuiImageData>> GuiBindableDataGrid::GetSmallImageProperty()
			{
				return dataProvider->smallImageProperty;
			}

			void GuiBindableDataGrid::SetSmallImageProperty(const ItemProperty<Ptr<GuiImageData>>& value)
			{
				if (dataProvider->smallImageProperty != value)
				{
					dataProvider->smallImageProperty = value;
					dataProvider->NotifyAllItemsUpdate();
					SmallImagePropertyChanged.Execute(GetNotifyEventArguments());
				}
			}

			description::Value GuiBindableDataGrid::GetSelectedRowValue()
			{
				auto pos = GetSelectedCell();
				if (pos.row == -1 || pos.column == -1)
				{
					return Value();
				}
				return dataProvider->GetBindingValue(GetSelectedCell().row);
			}

			description::Value GuiBindableDataGrid::GetSelectedCellValue()
			{
				auto pos = GetSelectedCell();
				if (pos.row == -1 || pos.column == -1)
				{
					return Value();
				}
				return dataProvider->GetColumns()[pos.column]->GetCellValue(pos.row);
			}
		}
	}
}

/***********************************************************************
.\CONTROLS\LISTCONTROLPACKAGE\GUIBINDABLELISTCONTROLS.CPP
***********************************************************************/

namespace vl
{
	namespace presentation
	{
		namespace controls
		{
			using namespace collections;
			using namespace list;
			using namespace tree;
			using namespace reflection::description;
			using namespace templates;

/***********************************************************************
GuiBindableTextList::ItemSource
***********************************************************************/

			GuiBindableTextList::ItemSource::ItemSource()
			{
			}

			GuiBindableTextList::ItemSource::~ItemSource()
			{
				SetItemSource(nullptr);
			}

			Ptr<description::IValueEnumerable> GuiBindableTextList::ItemSource::GetItemSource()
			{
				return itemSource;
			}

			void GuiBindableTextList::ItemSource::SetItemSource(Ptr<description::IValueEnumerable> _itemSource)
			{
				vint oldCount = 0;
				if (itemSource)
				{
					oldCount = itemSource->GetCount();
				}
				if (itemChangedEventHandler)
				{
					auto ol = itemSource.Cast<IValueObservableList>();
					ol->ItemChanged.Remove(itemChangedEventHandler);
				}

				itemSource = nullptr;
				itemChangedEventHandler = nullptr;

				if (_itemSource)
				{
					if (auto ol = _itemSource.Cast<IValueObservableList>())
					{
						itemSource = ol;
						itemChangedEventHandler = ol->ItemChanged.Add([this](vint start, vint oldCount, vint newCount)
						{
							InvokeOnItemModified(start, oldCount, newCount);
						});
					}
					else if (auto rl = _itemSource.Cast<IValueReadonlyList>())
					{
						itemSource = rl;
					}
					else
					{
						itemSource = IValueList::Create(GetLazyList<Value>(_itemSource));
					}
				}

				InvokeOnItemModified(0, oldCount, itemSource ? itemSource->GetCount() : 0);
			}

			description::Value GuiBindableTextList::ItemSource::Get(vint index)
			{
				if (!itemSource) return Value();
				return itemSource->Get(index);
			}

			void GuiBindableTextList::ItemSource::UpdateBindingProperties()
			{
				InvokeOnItemModified(0, Count(), Count());
			}
					
			// ===================== GuiListControl::IItemProvider =====================
			
			vint GuiBindableTextList::ItemSource::Count()
			{
				if (!itemSource) return 0;
				return itemSource->GetCount();
			}

			WString GuiBindableTextList::ItemSource::GetTextValue(vint itemIndex)
			{
				if (itemSource)
				{
					if (0 <= itemIndex && itemIndex < itemSource->GetCount())
					{
						return ReadProperty(itemSource->Get(itemIndex), textProperty);
					}
				}
				return L"";
			}
			
			IDescriptable* GuiBindableTextList::ItemSource::RequestView(const WString& identifier)
			{
				if (identifier == ITextItemView::Identifier)
				{
					return (ITextItemView*)this;
				}
				else
				{
					return 0;
				}
			}
					
			// ===================== GuiListControl::IItemBindingView =====================

			description::Value GuiBindableTextList::ItemSource::GetBindingValue(vint itemIndex)
			{
				if (itemSource)
				{
					if (0 <= itemIndex && itemIndex < itemSource->GetCount())
					{
						return itemSource->Get(itemIndex);
					}
				}
				return Value();
			}
					
			// ===================== list::TextItemStyleProvider::ITextItemView =====================
			
			bool GuiBindableTextList::ItemSource::GetChecked(vint itemIndex)
			{
				if (itemSource)
				{
					if (0 <= itemIndex && itemIndex < itemSource->GetCount())
					{
						return ReadProperty(itemSource->Get(itemIndex), checkedProperty);
					}
				}
				return false;
			}
			
			void GuiBindableTextList::ItemSource::SetChecked(vint itemIndex, bool value)
			{
				if (itemSource)
				{
					if (0 <= itemIndex && itemIndex < itemSource->GetCount())
					{
						auto thisValue = itemSource->Get(itemIndex);
						WriteProperty(thisValue, checkedProperty, value);
						InvokeOnItemModified(itemIndex, 1, 1);
					}
				}
			}

/***********************************************************************
GuiBindableTextList
***********************************************************************/

			GuiBindableTextList::GuiBindableTextList(theme::ThemeName themeName)
				:GuiVirtualTextList(themeName, new ItemSource)
			{
				itemSource = dynamic_cast<ItemSource*>(GetItemProvider());

				TextPropertyChanged.SetAssociatedComposition(boundsComposition);
				TextPropertyChanged.SetAssociatedComposition(boundsComposition);
			}

			GuiBindableTextList::~GuiBindableTextList()
			{
			}

			Ptr<description::IValueEnumerable> GuiBindableTextList::GetItemSource()
			{
				return itemSource->GetItemSource();
			}

			void GuiBindableTextList::SetItemSource(Ptr<description::IValueEnumerable> _itemSource)
			{
				itemSource->SetItemSource(_itemSource);
			}

			ItemProperty<WString> GuiBindableTextList::GetTextProperty()
			{
				return itemSource->textProperty;
			}

			void GuiBindableTextList::SetTextProperty(const ItemProperty<WString>& value)
			{
				if (itemSource->textProperty != value)
				{
					itemSource->textProperty = value;
					itemSource->UpdateBindingProperties();
					TextPropertyChanged.Execute(GetNotifyEventArguments());
				}
			}

			WritableItemProperty<bool> GuiBindableTextList::GetCheckedProperty()
			{
				return itemSource->checkedProperty;
			}

			void GuiBindableTextList::SetCheckedProperty(const WritableItemProperty<bool>& value)
			{
				if (itemSource->checkedProperty != value)
				{
					itemSource->checkedProperty = value;
					itemSource->UpdateBindingProperties();
					CheckedPropertyChanged.Execute(GetNotifyEventArguments());
				}
			}

			description::Value GuiBindableTextList::GetSelectedItem()
			{
				vint index = GetSelectedItemIndex();
				if (index == -1) return Value();
				return itemSource->Get(index);
			}

/***********************************************************************
GuiBindableListView::ItemSource
***********************************************************************/

			GuiBindableListView::ItemSource::ItemSource()
				:columns(this)
				, dataColumns(this)
			{
			}

			GuiBindableListView::ItemSource::~ItemSource()
			{
				SetItemSource(nullptr);
			}

			Ptr<description::IValueEnumerable> GuiBindableListView::ItemSource::GetItemSource()
			{
				return itemSource;
			}

			void GuiBindableListView::ItemSource::SetItemSource(Ptr<description::IValueEnumerable> _itemSource)
			{
				vint oldCount = 0;
				if (itemSource)
				{
					oldCount = itemSource->GetCount();
				}
				if (itemChangedEventHandler)
				{
					auto ol = itemSource.Cast<IValueObservableList>();
					ol->ItemChanged.Remove(itemChangedEventHandler);
				}

				itemSource = nullptr;
				itemChangedEventHandler = nullptr;

				if (_itemSource)
				{
					if (auto ol = _itemSource.Cast<IValueObservableList>())
					{
						itemSource = ol;
						itemChangedEventHandler = ol->ItemChanged.Add([this](vint start, vint oldCount, vint newCount)
						{
							InvokeOnItemModified(start, oldCount, newCount);
						});
					}
					else if (auto rl = _itemSource.Cast<IValueReadonlyList>())
					{
						itemSource = rl;
					}
					else
					{
						itemSource = IValueList::Create(GetLazyList<Value>(_itemSource));
					}
				}

				InvokeOnItemModified(0, oldCount, itemSource ? itemSource->GetCount() : 0);
			}

			description::Value GuiBindableListView::ItemSource::Get(vint index)
			{
				if (!itemSource) return Value();
				return itemSource->Get(index);
			}

			void GuiBindableListView::ItemSource::UpdateBindingProperties()
			{
				InvokeOnItemModified(0, Count(), Count());
			}

			bool GuiBindableListView::ItemSource::NotifyUpdate(vint start, vint count)
			{
				if (!itemSource) return false;
				if (start<0 || start >= itemSource->GetCount() || count <= 0 || start + count > itemSource->GetCount())
				{
					return false;
				}
				else
				{
					InvokeOnItemModified(start, count, count);
					return true;
				}
			}

			list::ListViewDataColumns& GuiBindableListView::ItemSource::GetDataColumns()
			{
				return dataColumns;
			}

			list::ListViewColumns& GuiBindableListView::ItemSource::GetColumns()
			{
				return columns;
			}
					
			// ===================== list::IListViewItemProvider =====================

			void GuiBindableListView::ItemSource::NotifyAllItemsUpdate()
			{
				NotifyUpdate(0, Count());
			}

			void GuiBindableListView::ItemSource::NotifyAllColumnsUpdate()
			{
				for (vint i = 0; i < columnItemViewCallbacks.Count(); i++)
				{
					columnItemViewCallbacks[i]->OnColumnChanged();
				}
			}

			// ===================== GuiListControl::IItemProvider =====================

			vint GuiBindableListView::ItemSource::Count()
			{
				if (!itemSource) return 0;
				return itemSource->GetCount();
			}

			WString GuiBindableListView::ItemSource::GetTextValue(vint itemIndex)
			{
				return GetText(itemIndex);
			}

			description::Value GuiBindableListView::ItemSource::GetBindingValue(vint itemIndex)
			{
				if (itemSource)
				{
					if (0 <= itemIndex && itemIndex < itemSource->GetCount())
					{
						return itemSource->Get(itemIndex);
					}
				}
				return Value();
			}

			IDescriptable* GuiBindableListView::ItemSource::RequestView(const WString& identifier)
			{
				if (identifier == IListViewItemView::Identifier)
				{
					return (IListViewItemView*)this;
				}
				else if (identifier == ListViewColumnItemArranger::IColumnItemView::Identifier)
				{
					return (ListViewColumnItemArranger::IColumnItemView*)this;
				}
				else
				{
					return 0;
				}
			}

			// ===================== list::ListViewItemStyleProvider::IListViewItemView =====================

			Ptr<GuiImageData> GuiBindableListView::ItemSource::GetSmallImage(vint itemIndex)
			{
				if (itemSource)
				{
					if (0 <= itemIndex && itemIndex < itemSource->GetCount())
					{
						return ReadProperty(itemSource->Get(itemIndex), smallImageProperty);
					}
				}
				return nullptr;
			}

			Ptr<GuiImageData> GuiBindableListView::ItemSource::GetLargeImage(vint itemIndex)
			{
				if (itemSource)
				{
					if (0 <= itemIndex && itemIndex < itemSource->GetCount())
					{
						return ReadProperty(itemSource->Get(itemIndex), largeImageProperty);
					}
				}
				return nullptr;
			}

			WString GuiBindableListView::ItemSource::GetText(vint itemIndex)
			{
				if (itemSource)
				{
					if (0 <= itemIndex && itemIndex < itemSource->GetCount() && columns.Count()>0)
					{
						return ReadProperty(itemSource->Get(itemIndex), columns[0]->GetTextProperty());
					}
				}
				return L"";
			}

			WString GuiBindableListView::ItemSource::GetSubItem(vint itemIndex, vint index)
			{
				if (itemSource)
				{
					if (0 <= itemIndex && itemIndex < itemSource->GetCount() && 0 <= index && index < columns.Count() - 1)
					{
						return ReadProperty(itemSource->Get(itemIndex), columns[index + 1]->GetTextProperty());
					}
				}
				return L"";
			}

			vint GuiBindableListView::ItemSource::GetDataColumnCount()
			{
				return dataColumns.Count();
			}

			vint GuiBindableListView::ItemSource::GetDataColumn(vint index)
			{
				return dataColumns[index];
			}

			vint GuiBindableListView::ItemSource::GetColumnCount()
			{
				return columns.Count();
			}

			WString GuiBindableListView::ItemSource::GetColumnText(vint index)
			{
				if (index < 0 || index >= columns.Count())
				{
					return L"";
				}
				else
				{
					return columns[index]->GetText();
				}
			}

			// ===================== list::ListViewColumnItemArranger::IColumnItemView =====================

			bool GuiBindableListView::ItemSource::AttachCallback(ListViewColumnItemArranger::IColumnItemViewCallback* value)
			{
				if(columnItemViewCallbacks.Contains(value))
				{
					return false;
				}
				else
				{
					columnItemViewCallbacks.Add(value);
					return true;
				}
			}

			bool GuiBindableListView::ItemSource::DetachCallback(ListViewColumnItemArranger::IColumnItemViewCallback* value)
			{
				vint index = columnItemViewCallbacks.IndexOf(value);
				if (index == -1)
				{
					return false;
				}
				else
				{
					columnItemViewCallbacks.Remove(value);
					return true;
				}
			}

			vint GuiBindableListView::ItemSource::GetColumnSize(vint index)
			{
				if (index < 0 || index >= columns.Count())
				{
					return 0;
				}
				else
				{
					return columns[index]->GetSize();
				}
			}

			void GuiBindableListView::ItemSource::SetColumnSize(vint index, vint value)
			{
				if (index >= 0 && index < columns.Count())
				{
					columns[index]->SetSize(value);
				}
			}

			GuiMenu* GuiBindableListView::ItemSource::GetDropdownPopup(vint index)
			{
				if (index < 0 || index >= columns.Count())
				{
					return 0;
				}
				else
				{
					return columns[index]->GetDropdownPopup();
				}
			}

			ColumnSortingState GuiBindableListView::ItemSource::GetSortingState(vint index)
			{
				if (index < 0 || index >= columns.Count())
				{
					return ColumnSortingState::NotSorted;
				}
				else
				{
					return columns[index]->GetSortingState();
				}
			}

/***********************************************************************
GuiBindableListView
***********************************************************************/

			GuiBindableListView::GuiBindableListView(theme::ThemeName themeName)
				:GuiVirtualListView(themeName, new ItemSource)
			{
				itemSource = dynamic_cast<ItemSource*>(GetItemProvider());

				LargeImagePropertyChanged.SetAssociatedComposition(boundsComposition);
				SmallImagePropertyChanged.SetAssociatedComposition(boundsComposition);
			}

			GuiBindableListView::~GuiBindableListView()
			{
			}

			list::ListViewDataColumns& GuiBindableListView::GetDataColumns()
			{
				return itemSource->GetDataColumns();
			}

			list::ListViewColumns& GuiBindableListView::GetColumns()
			{
				return itemSource->GetColumns();
			}

			Ptr<description::IValueEnumerable> GuiBindableListView::GetItemSource()
			{
				return itemSource->GetItemSource();
			}

			void GuiBindableListView::SetItemSource(Ptr<description::IValueEnumerable> _itemSource)
			{
				itemSource->SetItemSource(_itemSource);
			}

			ItemProperty<Ptr<GuiImageData>> GuiBindableListView::GetLargeImageProperty()
			{
				return itemSource->largeImageProperty;
			}

			void GuiBindableListView::SetLargeImageProperty(const ItemProperty<Ptr<GuiImageData>>& value)
			{
				if (itemSource->largeImageProperty != value)
				{
					itemSource->largeImageProperty = value;
					itemSource->UpdateBindingProperties();
					LargeImagePropertyChanged.Execute(GetNotifyEventArguments());
				}
			}

			ItemProperty<Ptr<GuiImageData>> GuiBindableListView::GetSmallImageProperty()
			{
				return itemSource->smallImageProperty;
			}

			void GuiBindableListView::SetSmallImageProperty(const ItemProperty<Ptr<GuiImageData>>& value)
			{
				if (itemSource->smallImageProperty != value)
				{
					itemSource->smallImageProperty = value;
					itemSource->UpdateBindingProperties();
					SmallImagePropertyChanged.Execute(GetNotifyEventArguments());
				}
			}

			description::Value GuiBindableListView::GetSelectedItem()
			{
				vint index = GetSelectedItemIndex();
				if (index == -1) return Value();
				return itemSource->Get(index);
			}

/***********************************************************************
GuiBindableTreeView::ItemSourceNode
***********************************************************************/

			void GuiBindableTreeView::ItemSourceNode::PrepareChildren()
			{
				if (!childrenVirtualList)
				{
					if (auto value = ReadProperty(itemSource, rootProvider->childrenProperty))
					{
						if (auto ol = value.Cast<IValueObservableList>())
						{
							itemChangedEventHandler = ol->ItemChanged.Add([this](vint start, vint oldCount, vint newCount)
							{
								callback->OnBeforeItemModified(this, start, oldCount, newCount);
								children.RemoveRange(start, oldCount);
								for (vint i = 0; i < newCount; i++)
								{
									Value value = childrenVirtualList->Get(start + i);
									auto node = new ItemSourceNode(value, this);
									children.Insert(start + i, node);
								}
								callback->OnAfterItemModified(this, start, oldCount, newCount);
							});
							childrenVirtualList = ol;
						}
						else if (auto rl = value.Cast<IValueReadonlyList>())
						{
							childrenVirtualList = rl;
						}
						else
						{
							childrenVirtualList = IValueList::Create(GetLazyList<Value>(value));
						}
					}

					if (!childrenVirtualList)
					{
						childrenVirtualList = IValueList::Create();
					}

					vint count = childrenVirtualList->GetCount();
					for (vint i = 0; i < count; i++)
					{
						Value value = childrenVirtualList->Get(i);
						auto node = new ItemSourceNode(value, this);
						children.Add(node);
					}
				}
			}

			void GuiBindableTreeView::ItemSourceNode::UnprepareChildren()
			{
				if (itemChangedEventHandler)
				{
					auto ol = childrenVirtualList.Cast<IValueObservableList>();
					ol->ItemChanged.Remove(itemChangedEventHandler);
					itemChangedEventHandler = nullptr;
				}
				childrenVirtualList = nullptr;
				FOREACH(Ptr<ItemSourceNode>, node, children)
				{
					node->UnprepareChildren();
				}
				children.Clear();
			}

			GuiBindableTreeView::ItemSourceNode::ItemSourceNode(const description::Value& _itemSource, ItemSourceNode* _parent)
				:itemSource(_itemSource)
				, rootProvider(_parent->rootProvider)
				, parent(_parent)
				, callback(_parent->callback)
			{
			}

			GuiBindableTreeView::ItemSourceNode::ItemSourceNode(ItemSource* _rootProvider)
				:rootProvider(_rootProvider)
				, parent(nullptr)
				, callback(_rootProvider)
			{
			}

			GuiBindableTreeView::ItemSourceNode::~ItemSourceNode()
			{
			}

			description::Value GuiBindableTreeView::ItemSourceNode::GetItemSource()
			{
				return itemSource;
			}

			void GuiBindableTreeView::ItemSourceNode::SetItemSource(const description::Value& _itemSource)
			{
				vint oldCount = GetChildCount();
				UnprepareChildren();
				itemSource = _itemSource;
				vint newCount = GetChildCount();
				callback->OnBeforeItemModified(this, 0, oldCount, newCount);
				callback->OnAfterItemModified(this, 0, oldCount, newCount);
			}

			bool GuiBindableTreeView::ItemSourceNode::GetExpanding()
			{
				return this == rootProvider->rootNode.Obj() ? true : expanding;
			}

			void GuiBindableTreeView::ItemSourceNode::SetExpanding(bool value)
			{
				if (this != rootProvider->rootNode.Obj() && expanding != value)
				{
					expanding = value;
					if (expanding)
					{
						callback->OnItemExpanded(this);
					}
					else
					{
						callback->OnItemCollapsed(this);
					}
				}
			}

			vint GuiBindableTreeView::ItemSourceNode::CalculateTotalVisibleNodes()
			{
				if (!GetExpanding())
				{
					return 1;
				}

				PrepareChildren();
				vint count = 1;
				FOREACH(Ptr<ItemSourceNode>, child, children)
				{
					count += child->CalculateTotalVisibleNodes();
				}
				return count;
			}

			vint GuiBindableTreeView::ItemSourceNode::GetChildCount()
			{
				PrepareChildren();
				return children.Count();
			}

			Ptr<tree::INodeProvider> GuiBindableTreeView::ItemSourceNode::GetParent()
			{
				return parent;
			}

			Ptr<tree::INodeProvider> GuiBindableTreeView::ItemSourceNode::GetChild(vint index)
			{
				PrepareChildren();
				if (0 <= index && index < children.Count())
				{
					return children[index];
				}
				return nullptr;
			}

/***********************************************************************
GuiBindableTreeView::ItemSource
***********************************************************************/

			GuiBindableTreeView::ItemSource::ItemSource()
			{
				rootNode = new ItemSourceNode(this);
			}

			GuiBindableTreeView::ItemSource::~ItemSource()
			{
			}

			description::Value GuiBindableTreeView::ItemSource::GetItemSource()
			{
				return rootNode->GetItemSource();
			}

			void GuiBindableTreeView::ItemSource::SetItemSource(const description::Value& _itemSource)
			{
				rootNode->SetItemSource(_itemSource);
			}

			void GuiBindableTreeView::ItemSource::UpdateBindingProperties(bool updateChildrenProperty)
			{
				vint oldCount = rootNode->GetChildCount();
				if (updateChildrenProperty)
				{
					rootNode->UnprepareChildren();
				}
				vint newCount = rootNode->GetChildCount();
				OnBeforeItemModified(rootNode.Obj(), 0, oldCount, newCount);
				OnAfterItemModified(rootNode.Obj(), 0, oldCount, newCount);
			}

			// ===================== tree::INodeRootProvider =====================

			Ptr<tree::INodeProvider> GuiBindableTreeView::ItemSource::GetRootNode()
			{
				return rootNode;
			}

			WString GuiBindableTreeView::ItemSource::GetTextValue(tree::INodeProvider* node)
			{
				return ReadProperty(GetBindingValue(node), textProperty);
			}

			description::Value GuiBindableTreeView::ItemSource::GetBindingValue(tree::INodeProvider* node)
			{
				if (auto itemSourceNode = dynamic_cast<ItemSourceNode*>(node))
				{
					return itemSourceNode->GetItemSource();
				}
				return Value();
			}

			IDescriptable* GuiBindableTreeView::ItemSource::RequestView(const WString& identifier)
			{
				if(identifier==ITreeViewItemView::Identifier)
				{
					return (ITreeViewItemView*)this;
				}
				else
				{
					return 0;
				}
			}

			// ===================== tree::ITreeViewItemView =====================

			Ptr<GuiImageData> GuiBindableTreeView::ItemSource::GetNodeImage(tree::INodeProvider* node)
			{
				if (auto itemSourceNode = dynamic_cast<ItemSourceNode*>(node))
				{
					return ReadProperty(itemSourceNode->GetItemSource(), imageProperty);
				}
				return nullptr;
			}

/***********************************************************************
GuiBindableTreeView
***********************************************************************/

			GuiBindableTreeView::GuiBindableTreeView(theme::ThemeName themeName)
				:GuiVirtualTreeView(themeName, new ItemSource)
			{
				itemSource = dynamic_cast<ItemSource*>(GetNodeRootProvider());

				TextPropertyChanged.SetAssociatedComposition(boundsComposition);
				ImagePropertyChanged.SetAssociatedComposition(boundsComposition);
				ChildrenPropertyChanged.SetAssociatedComposition(boundsComposition);
			}

			GuiBindableTreeView::~GuiBindableTreeView()
			{
			}

			description::Value GuiBindableTreeView::GetItemSource()
			{
				return itemSource->GetItemSource();
			}

			void GuiBindableTreeView::SetItemSource(description::Value _itemSource)
			{
				itemSource->SetItemSource(_itemSource);
			}

			ItemProperty<WString> GuiBindableTreeView::GetTextProperty()
			{
				return itemSource->textProperty;
			}

			void GuiBindableTreeView::SetTextProperty(const ItemProperty<WString>& value)
			{
				if (itemSource->textProperty != value)
				{
					itemSource->textProperty = value;
					itemSource->UpdateBindingProperties(false);
					TextPropertyChanged.Execute(GetNotifyEventArguments());
				}
			}

			ItemProperty<Ptr<GuiImageData>> GuiBindableTreeView::GetImageProperty()
			{
				return itemSource->imageProperty;
			}

			void GuiBindableTreeView::SetImageProperty(const ItemProperty<Ptr<GuiImageData>>& value)
			{
				if (itemSource->imageProperty != value)
				{
					itemSource->imageProperty = value;
					itemSource->UpdateBindingProperties(false);
					ImagePropertyChanged.Execute(GetNotifyEventArguments());
				}
			}

			ItemProperty<Ptr<IValueEnumerable>> GuiBindableTreeView::GetChildrenProperty()
			{
				return itemSource->childrenProperty;
			}

			void GuiBindableTreeView::SetChildrenProperty(const ItemProperty<Ptr<IValueEnumerable>>& value)
			{
				if (itemSource->childrenProperty != value)
				{
					itemSource->childrenProperty = value;
					itemSource->UpdateBindingProperties(true);
					ChildrenPropertyChanged.Execute(GetNotifyEventArguments());
				}
			}

			description::Value GuiBindableTreeView::GetSelectedItem()
			{
				vint index = GetSelectedItemIndex();
				if (index == -1) return Value();

				Value result;
				if (auto node = nodeItemView->RequestNode(index))
				{
					if (auto itemSourceNode = node.Cast<ItemSourceNode>())
					{
						result = itemSourceNode->GetItemSource();
					}
				}
				return result;
			}
		}
	}
}


/***********************************************************************
.\CONTROLS\LISTCONTROLPACKAGE\GUILISTVIEWCONTROLS.CPP
***********************************************************************/

namespace vl
{
	namespace presentation
	{
		namespace controls
		{
			using namespace elements;
			using namespace compositions;
			using namespace collections;
			using namespace reflection::description;

/***********************************************************************
GuiListViewColumnHeader
***********************************************************************/

			void GuiListViewColumnHeader::BeforeControlTemplateUninstalled_()
			{
			}

			void GuiListViewColumnHeader::AfterControlTemplateInstalled_(bool initialize)
			{
				GetControlTemplateObject(true)->SetSortingState(columnSortingState);
			}
			
			GuiListViewColumnHeader::GuiListViewColumnHeader(theme::ThemeName themeName)
				:GuiMenuButton(themeName)
			{
			}

			GuiListViewColumnHeader::~GuiListViewColumnHeader()
			{
			}

			bool GuiListViewColumnHeader::IsAltAvailable()
			{
				return false;
			}

			ColumnSortingState GuiListViewColumnHeader::GetColumnSortingState()
			{
				return columnSortingState;
			}

			void GuiListViewColumnHeader::SetColumnSortingState(ColumnSortingState value)
			{
				if (columnSortingState != value)
				{
					columnSortingState = value;
					GetControlTemplateObject(true)->SetSortingState(columnSortingState);
				}
			}

/***********************************************************************
GuiListViewBase
***********************************************************************/

			void GuiListViewBase::BeforeControlTemplateUninstalled_()
			{
			}

			void GuiListViewBase::AfterControlTemplateInstalled_(bool initialize)
			{
			}

			GuiListViewBase::GuiListViewBase(theme::ThemeName themeName, GuiListControl::IItemProvider* _itemProvider)
				:GuiSelectableListControl(themeName, _itemProvider)
			{
				ColumnClicked.SetAssociatedComposition(boundsComposition);
			}

			GuiListViewBase::~GuiListViewBase()
			{
			}

			namespace list
			{

				const wchar_t* const IListViewItemView::Identifier = L"vl::presentation::controls::list::IListViewItemView";
				
/***********************************************************************
ListViewColumnItemArranger::ColumnItemViewCallback
***********************************************************************/

				ListViewColumnItemArranger::ColumnItemViewCallback::ColumnItemViewCallback(ListViewColumnItemArranger* _arranger)
					:arranger(_arranger)
				{
				}

				ListViewColumnItemArranger::ColumnItemViewCallback::~ColumnItemViewCallback()
				{
				}

				void ListViewColumnItemArranger::ColumnItemViewCallback::OnColumnChanged()
				{
					arranger->RebuildColumns();
					FOREACH(ItemStyleRecord, style, arranger->visibleStyles)
					{
						if (auto callback = dynamic_cast<IColumnItemViewCallback*>(style.key))
						{
							callback->OnColumnChanged();
						}
					}
				}
				
/***********************************************************************
ListViewColumnItemArranger
***********************************************************************/

				const wchar_t* const ListViewColumnItemArranger::IColumnItemView::Identifier = L"vl::presentation::controls::list::ListViewColumnItemArranger::IColumnItemView";

				void ListViewColumnItemArranger::ColumnClicked(vint index, compositions::GuiGraphicsComposition* sender, compositions::GuiEventArgs& arguments)
				{
					GuiItemEventArgs args(listView->ColumnClicked.GetAssociatedComposition());
					args.itemIndex=index;
					listView->ColumnClicked.Execute(args);
				}

				void ListViewColumnItemArranger::ColumnBoundsChanged(vint index, compositions::GuiGraphicsComposition* sender, compositions::GuiEventArgs& arguments)
				{
					GuiBoundsComposition* buttonBounds=columnHeaderButtons[index]->GetBoundsComposition();
					vint size=buttonBounds->GetBounds().Width();
					if(size>columnItemView->GetColumnSize(index))
					{
						columnItemView->SetColumnSize(index, size);
					}
				}

				void ListViewColumnItemArranger::ColumnHeaderSplitterLeftButtonDown(compositions::GuiGraphicsComposition* sender, compositions::GuiMouseEventArgs& arguments)
				{
					if(listView->GetVisuallyEnabled())
					{
						arguments.handled=true;
						splitterDragging=true;
						splitterLatestX=arguments.x;
					}
				}

				void ListViewColumnItemArranger::ColumnHeaderSplitterLeftButtonUp(compositions::GuiGraphicsComposition* sender, compositions::GuiMouseEventArgs& arguments)
				{
					if(listView->GetVisuallyEnabled())
					{
						arguments.handled=true;
						splitterDragging=false;
						splitterLatestX=0;
					}
				}

				void ListViewColumnItemArranger::ColumnHeaderSplitterMouseMove(compositions::GuiGraphicsComposition* sender, compositions::GuiMouseEventArgs& arguments)
				{
					if(splitterDragging)
					{
						vint offset=arguments.x-splitterLatestX;
						vint index=columnHeaderSplitters.IndexOf(dynamic_cast<GuiBoundsComposition*>(sender));
						if(index!=-1)
						{
							GuiBoundsComposition* buttonBounds=columnHeaderButtons[index]->GetBoundsComposition();
							Rect bounds=buttonBounds->GetBounds();
							Rect newBounds(bounds.LeftTop(), Size(bounds.Width()+offset, bounds.Height()));
							buttonBounds->SetBounds(newBounds);

							vint finalSize=buttonBounds->GetBounds().Width();
							columnItemView->SetColumnSize(index, finalSize);
						}
					}
				}

				void ListViewColumnItemArranger::RearrangeItemBounds()
				{
					FixedHeightItemArranger::RearrangeItemBounds();
					vint count = columnHeaders->GetParent()->Children().Count();
					columnHeaders->GetParent()->MoveChild(columnHeaders, count - 1);
					columnHeaders->SetBounds(Rect(Point(-viewBounds.Left(), 0), Size(0, 0)));
				}

				vint ListViewColumnItemArranger::GetWidth()
				{
					vint width=columnHeaders->GetBounds().Width()-SplitterWidth;
					if(width<SplitterWidth)
					{
						width=SplitterWidth;
					}
					return width;
				}

				vint ListViewColumnItemArranger::GetYOffset()
				{
					return columnHeaders->GetBounds().Height();
				}

				Size ListViewColumnItemArranger::OnCalculateTotalSize()
				{
					Size size=FixedHeightItemArranger::OnCalculateTotalSize();
					size.x+=SplitterWidth;
					return size;
				}

				void ListViewColumnItemArranger::DeleteColumnButtons()
				{
					for(vint i=columnHeaders->GetStackItems().Count()-1;i>=0;i--)
					{
						GuiStackItemComposition* item=columnHeaders->GetStackItems().Get(i);
						columnHeaders->RemoveChild(item);

						GuiControl* button=item->Children().Get(0)->GetAssociatedControl();
						if(button)
						{
							item->RemoveChild(button->GetBoundsComposition());
							delete button;
						}
						delete item;
					}
					columnHeaderButtons.Clear();
					columnHeaderSplitters.Clear();
				}

				void ListViewColumnItemArranger::RebuildColumns()
				{
					if (columnItemView && columnHeaderButtons.Count() == listViewItemView->GetColumnCount())
					{
						for (vint i = 0; i < listViewItemView->GetColumnCount(); i++)
						{
							GuiListViewColumnHeader* button = columnHeaderButtons[i];
							button->SetText(listViewItemView->GetColumnText(i));
							button->SetSubMenu(columnItemView->GetDropdownPopup(i), false);
							button->SetColumnSortingState(columnItemView->GetSortingState(i));
							button->GetBoundsComposition()->SetBounds(Rect(Point(0, 0), Size(columnItemView->GetColumnSize(i), 0)));
						}
					}
					else
					{
						DeleteColumnButtons();
						if (columnItemView && listViewItemView)
						{
							for (vint i = 0; i < listViewItemView->GetColumnCount(); i++)
							{
								GuiBoundsComposition* splitterComposition = new GuiBoundsComposition;
								splitterComposition->SetAlignmentToParent(Margin(0, 0, 0, 0));
								splitterComposition->SetAssociatedCursor(GetCurrentController()->ResourceService()->GetSystemCursor(INativeCursor::SizeWE));
								splitterComposition->SetAlignmentToParent(Margin(0, 0, -1, 0));
								splitterComposition->SetPreferredMinSize(Size(SplitterWidth, 0));
								columnHeaderSplitters.Add(splitterComposition);

								splitterComposition->GetEventReceiver()->leftButtonDown.AttachMethod(this, &ListViewColumnItemArranger::ColumnHeaderSplitterLeftButtonDown);
								splitterComposition->GetEventReceiver()->leftButtonUp.AttachMethod(this, &ListViewColumnItemArranger::ColumnHeaderSplitterLeftButtonUp);
								splitterComposition->GetEventReceiver()->mouseMove.AttachMethod(this, &ListViewColumnItemArranger::ColumnHeaderSplitterMouseMove);
							}
							for (vint i = 0; i < listViewItemView->GetColumnCount(); i++)
							{
								GuiListViewColumnHeader* button = new GuiListViewColumnHeader(theme::ThemeName::Unknown);
								button->SetControlTemplate(listView->GetControlTemplateObject(true)->GetColumnHeaderTemplate());
								button->SetText(listViewItemView->GetColumnText(i));
								button->SetSubMenu(columnItemView->GetDropdownPopup(i), false);
								button->SetColumnSortingState(columnItemView->GetSortingState(i));
								button->GetBoundsComposition()->SetBounds(Rect(Point(0, 0), Size(columnItemView->GetColumnSize(i), 0)));
								button->Clicked.AttachLambda(Curry(Func<void(vint, GuiGraphicsComposition*, GuiEventArgs&)>(this, &ListViewColumnItemArranger::ColumnClicked))(i));
								button->GetBoundsComposition()->BoundsChanged.AttachLambda(Curry(Func<void(vint, GuiGraphicsComposition*, GuiEventArgs&)>(this, &ListViewColumnItemArranger::ColumnBoundsChanged))(i));
								columnHeaderButtons.Add(button);
								if (i > 0)
								{
									button->GetContainerComposition()->AddChild(columnHeaderSplitters[i - 1]);
								}

								GuiStackItemComposition* item = new GuiStackItemComposition;
								item->SetMinSizeLimitation(GuiGraphicsComposition::LimitToElementAndChildren);
								item->AddChild(button->GetBoundsComposition());
								columnHeaders->AddChild(item);
							}
							if (listViewItemView->GetColumnCount() > 0)
							{
								GuiBoundsComposition* splitterComposition = columnHeaderSplitters[listViewItemView->GetColumnCount() - 1];

								GuiStackItemComposition* item = new GuiStackItemComposition;
								item->SetMinSizeLimitation(GuiGraphicsComposition::LimitToElementAndChildren);
								item->AddChild(splitterComposition);
								columnHeaders->AddChild(item);
							}
						}
					}
					callback->OnTotalSizeChanged();
				}

				ListViewColumnItemArranger::ListViewColumnItemArranger()
				{
					columnHeaders = new GuiStackComposition;
					columnHeaders->SetMinSizeLimitation(GuiGraphicsComposition::LimitToElementAndChildren);
					columnItemViewCallback = new ColumnItemViewCallback(this);
				}

				ListViewColumnItemArranger::~ListViewColumnItemArranger()
				{
					if(!columnHeaders->GetParent())
					{
						DeleteColumnButtons();
						delete columnHeaders;
					}
				}

				void ListViewColumnItemArranger::AttachListControl(GuiListControl* value)
				{
					FixedHeightItemArranger::AttachListControl(value);
					listView = dynamic_cast<GuiListViewBase*>(value);
					if (listView)
					{
						listView->GetContainerComposition()->AddChild(columnHeaders);
						listViewItemView = dynamic_cast<IListViewItemView*>(listView->GetItemProvider()->RequestView(IListViewItemView::Identifier));
						columnItemView = dynamic_cast<IColumnItemView*>(listView->GetItemProvider()->RequestView(IColumnItemView::Identifier));
						if (columnItemView)
						{
							columnItemView->AttachCallback(columnItemViewCallback.Obj());
							RebuildColumns();
						}
					}
				}

				void ListViewColumnItemArranger::DetachListControl()
				{
					if (listView)
					{
						if (columnItemView)
						{
							columnItemView->DetachCallback(columnItemViewCallback.Obj());
							columnItemView = nullptr;
						}
						listViewItemView = nullptr;
						listView->GetContainerComposition()->RemoveChild(columnHeaders);
						listView = nullptr;
					}
					FixedHeightItemArranger::DetachListControl();
				}

/***********************************************************************
ListViewSubItems
***********************************************************************/

				void ListViewSubItems::NotifyUpdateInternal(vint start, vint count, vint newCount)
				{
					owner->NotifyUpdate();
				}

/***********************************************************************
ListViewItem
***********************************************************************/

				void ListViewItem::NotifyUpdate()
				{
					if (owner)
					{
						vint index = owner->IndexOf(this);
						owner->NotifyUpdateInternal(index, 1, 1);
					}
				}

				ListViewItem::ListViewItem()
					:owner(0)
				{
					subItems.owner = this;
				}

				ListViewSubItems& ListViewItem::GetSubItems()
				{
					return subItems;
				}

				Ptr<GuiImageData> ListViewItem::GetSmallImage()
				{
					return smallImage;
				}

				void ListViewItem::SetSmallImage(Ptr<GuiImageData> value)
				{
					smallImage = value;
					NotifyUpdate();
				}

				Ptr<GuiImageData> ListViewItem::GetLargeImage()
				{
					return largeImage;
				}
				
				void ListViewItem::SetLargeImage(Ptr<GuiImageData> value)
				{
					largeImage = value;
					NotifyUpdate();
				}

				const WString& ListViewItem::GetText()
				{
					return text;
				}

				void ListViewItem::SetText(const WString& value)
				{
					text = value;
					NotifyUpdate();
				}

				description::Value ListViewItem::GetTag()
				{
					return tag;
				}

				void ListViewItem::SetTag(const description::Value& value)
				{
					tag = value;
					NotifyUpdate();
				}

/***********************************************************************
ListViewColumn
***********************************************************************/

				void ListViewColumn::NotifyUpdate(bool affectItem)
				{
					if (owner)
					{
						vint index = owner->IndexOf(this);
						owner->NotifyColumnUpdated(index, affectItem);
					}
				}

				ListViewColumn::ListViewColumn(const WString& _text, vint _size)
					:text(_text)
					,size(_size)
				{
				}

				ListViewColumn::~ListViewColumn()
				{
					if (dropdownPopup && ownPopup)
					{
						SafeDeleteControl(dropdownPopup);
					}
				}

				const WString& ListViewColumn::GetText()
				{
					return text;
				}

				void ListViewColumn::SetText(const WString& value)
				{
					if (text != value)
					{
						text = value;
						NotifyUpdate(false);
					}
				}

				ItemProperty<WString> ListViewColumn::GetTextProperty()
				{
					return textProperty;
				}

				void ListViewColumn::SetTextProperty(const ItemProperty<WString>& value)
				{
					textProperty = value;
					NotifyUpdate(true);
				}

				vint ListViewColumn::GetSize()
				{
					return size;
				}

				void ListViewColumn::SetSize(vint value)
				{
					if (size != value)
					{
						size = value;
						NotifyUpdate(false);
					}
				}

				bool ListViewColumn::GetOwnPopup()
				{
					return ownPopup;
				}

				void ListViewColumn::SetOwnPopup(bool value)
				{
					ownPopup = value;
				}

				GuiMenu* ListViewColumn::GetDropdownPopup()
				{
					return dropdownPopup;
				}

				void ListViewColumn::SetDropdownPopup(GuiMenu* value)
				{
					if (dropdownPopup != value)
					{
						dropdownPopup = value;
						NotifyUpdate(false);
					}
				}

				ColumnSortingState ListViewColumn::GetSortingState()
				{
					return sortingState;
				}

				void ListViewColumn::SetSortingState(ColumnSortingState value)
				{
					if (sortingState != value)
					{
						sortingState = value;
						NotifyUpdate(false);
					}
				}

/***********************************************************************
ListViewDataColumns
***********************************************************************/

				void ListViewDataColumns::NotifyUpdateInternal(vint start, vint count, vint newCount)
				{
					itemProvider->NotifyAllItemsUpdate();
				}

				ListViewDataColumns::ListViewDataColumns(IListViewItemProvider* _itemProvider)
					:itemProvider(_itemProvider)
				{
				}

				ListViewDataColumns::~ListViewDataColumns()
				{
				}

/***********************************************************************
ListViewColumns
***********************************************************************/

				void ListViewColumns::NotifyColumnUpdated(vint column, bool affectItem)
				{
					affectItemFlag = affectItem;
					NotifyUpdate(column, 1);
					affectItemFlag = true;
				}

				void ListViewColumns::AfterInsert(vint index, const Ptr<ListViewColumn>& value)
				{
					collections::ObservableListBase<Ptr<ListViewColumn>>::AfterInsert(index, value);
					value->owner = this;
				}

				void ListViewColumns::BeforeRemove(vint index, const Ptr<ListViewColumn>& value)
				{
					value->owner = 0;
					collections::ObservableListBase<Ptr<ListViewColumn>>::BeforeRemove(index, value);
				}

				void ListViewColumns::NotifyUpdateInternal(vint start, vint count, vint newCount)
				{
					itemProvider->NotifyAllColumnsUpdate();
					if (affectItemFlag)
					{
						itemProvider->NotifyAllItemsUpdate();
					}
				}

				ListViewColumns::ListViewColumns(IListViewItemProvider* _itemProvider)
					:itemProvider(_itemProvider)
				{
				}

				ListViewColumns::~ListViewColumns()
				{
				}

/***********************************************************************
ListViewItemProvider
***********************************************************************/

				void ListViewItemProvider::AfterInsert(vint index, const Ptr<ListViewItem>& value)
				{
					ListProvider<Ptr<ListViewItem>>::AfterInsert(index, value);
					value->owner = this;
				}

				void ListViewItemProvider::BeforeRemove(vint index, const Ptr<ListViewItem>& value)
				{
					value->owner = 0;
					ListProvider<Ptr<ListViewItem>>::AfterInsert(index, value);
				}

				void ListViewItemProvider::NotifyAllItemsUpdate()
				{
					NotifyUpdate(0, Count());
				}

				void ListViewItemProvider::NotifyAllColumnsUpdate()
				{
					for (vint i = 0; i < columnItemViewCallbacks.Count(); i++)
					{
						columnItemViewCallbacks[i]->OnColumnChanged();
					}
				}

				Ptr<GuiImageData> ListViewItemProvider::GetSmallImage(vint itemIndex)
				{
					return Get(itemIndex)->smallImage;
				}

				Ptr<GuiImageData> ListViewItemProvider::GetLargeImage(vint itemIndex)
				{
					return Get(itemIndex)->largeImage;
				}

				WString ListViewItemProvider::GetText(vint itemIndex)
				{
					return Get(itemIndex)->text;
				}

				WString ListViewItemProvider::GetSubItem(vint itemIndex, vint index)
				{
					Ptr<ListViewItem> item=Get(itemIndex);
					if(index<0 || index>=item->GetSubItems().Count())
					{
						return L"";
					}
					else
					{
						return item->GetSubItems()[index];
					}
				}

				vint ListViewItemProvider::GetDataColumnCount()
				{
					return dataColumns.Count();
				}

				vint ListViewItemProvider::GetDataColumn(vint index)
				{
					return dataColumns[index];
				}

				vint ListViewItemProvider::GetColumnCount()
				{
					return columns.Count();
				}

				WString ListViewItemProvider::GetColumnText(vint index)
				{
					if (index<0 || index >= columns.Count())
					{
						return L"";
					}
					else
					{
						return columns[index]->GetText();
					}
				}

				bool ListViewItemProvider::AttachCallback(ListViewColumnItemArranger::IColumnItemViewCallback* value)
				{
					if(columnItemViewCallbacks.Contains(value))
					{
						return false;
					}
					else
					{
						columnItemViewCallbacks.Add(value);
						return true;
					}
				}

				bool ListViewItemProvider::DetachCallback(ListViewColumnItemArranger::IColumnItemViewCallback* value)
				{
					vint index=columnItemViewCallbacks.IndexOf(value);
					if(index==-1)
					{
						return false;
					}
					else
					{
						columnItemViewCallbacks.Remove(value);
						return true;
					}
				}

				vint ListViewItemProvider::GetColumnSize(vint index)
				{
					if(index<0 || index>=columns.Count())
					{
						return 0;
					}
					else
					{
						return columns[index]->GetSize();
					}
				}

				void ListViewItemProvider::SetColumnSize(vint index, vint value)
				{
					if(index>=0 && index<columns.Count())
					{
						columns[index]->SetSize(value);
					}
				}

				GuiMenu* ListViewItemProvider::GetDropdownPopup(vint index)
				{
					if(index<0 || index>=columns.Count())
					{
						return 0;
					}
					else
					{
						return columns[index]->GetDropdownPopup();
					}
				}

				ColumnSortingState ListViewItemProvider::GetSortingState(vint index)
				{
					if (index < 0 || index >= columns.Count())
					{
						return ColumnSortingState::NotSorted;
					}
					else
					{
						return columns[index]->GetSortingState();
					}
				}

				WString ListViewItemProvider::GetTextValue(vint itemIndex)
				{
					return GetText(itemIndex);
				}

				description::Value ListViewItemProvider::GetBindingValue(vint itemIndex)
				{
					return Value::From(Get(itemIndex));
				}

				ListViewItemProvider::ListViewItemProvider()
					:columns(this)
					, dataColumns(this)
				{
				}

				ListViewItemProvider::~ListViewItemProvider()
				{
				}

				IDescriptable* ListViewItemProvider::RequestView(const WString& identifier)
				{
					if (identifier == IListViewItemView::Identifier)
					{
						return (IListViewItemView*)this;
					}
					else if (identifier == ListViewColumnItemArranger::IColumnItemView::Identifier)
					{
						return (ListViewColumnItemArranger::IColumnItemView*)this;
					}
					else
					{
						return 0;
					}
				}

				ListViewDataColumns& ListViewItemProvider::GetDataColumns()
				{
					return dataColumns;
				}

				ListViewColumns& ListViewItemProvider::GetColumns()
				{
					return columns;
				}
			}

/***********************************************************************
GuiListView
***********************************************************************/

			void GuiVirtualListView::OnStyleInstalled(vint itemIndex, ItemStyle* style)
			{
				GuiListViewBase::OnStyleInstalled(itemIndex, style);
			}

			void GuiVirtualListView::OnItemTemplateChanged(compositions::GuiGraphicsComposition* sender, compositions::GuiEventArgs& arguments)
			{
				view = ListViewView::Unknown;
			}

			GuiVirtualListView::GuiVirtualListView(theme::ThemeName themeName, GuiListControl::IItemProvider* _itemProvider)
				:GuiListViewBase(themeName, _itemProvider)
			{
				SetView(ListViewView::Detail);
			}

			GuiVirtualListView::~GuiVirtualListView()
			{
			}

			ListViewView GuiVirtualListView::GetView()
			{
				return view;
			}

			void GuiVirtualListView::SetView(ListViewView _view)
			{
				switch (_view)
				{
				case ListViewView::BigIcon:
					SetStyleAndArranger(
						[](const Value&) { return new list::BigIconListViewItemTemplate; },
						new list::FixedSizeMultiColumnItemArranger
						);
					break;
				case ListViewView::SmallIcon:
					SetStyleAndArranger(
						[](const Value&) { return new list::SmallIconListViewItemTemplate; },
						new list::FixedSizeMultiColumnItemArranger
					);
					break;
				case ListViewView::List:
					SetStyleAndArranger(
						[](const Value&) { return new list::ListListViewItemTemplate; },
						new list::FixedHeightMultiColumnItemArranger
					);
					break;
				case ListViewView::Tile:
					SetStyleAndArranger(
						[](const Value&) { return new list::TileListViewItemTemplate; },
						new list::FixedSizeMultiColumnItemArranger
					);
					break;
				case ListViewView::Information:
					SetStyleAndArranger(
						[](const Value&) { return new list::InformationListViewItemTemplate; },
						new list::FixedHeightItemArranger
					);
					break;
				case ListViewView::Detail:
					SetStyleAndArranger(
						[](const Value&) { return new list::DetailListViewItemTemplate; },
						new list::ListViewColumnItemArranger
					);
					break;
				default:;
				}
				view = _view;
			}

/***********************************************************************
GuiListView
***********************************************************************/

			GuiListView::GuiListView(theme::ThemeName themeName)
				:GuiVirtualListView(themeName, new list::ListViewItemProvider)
			{
				items=dynamic_cast<list::ListViewItemProvider*>(itemProvider.Obj());
			}

			GuiListView::~GuiListView()
			{
			}

			list::ListViewItemProvider& GuiListView::GetItems()
			{
				return *items;
			}

			list::ListViewDataColumns& GuiListView::GetDataColumns()
			{
				return items->GetDataColumns();
			}

			list::ListViewColumns& GuiListView::GetColumns()
			{
				return items->GetColumns();
			}

			Ptr<list::ListViewItem> GuiListView::GetSelectedItem()
			{
				vint index = GetSelectedItemIndex();
				if (index == -1) return 0;
				return items->Get(index);
			}
		}
	}
}

/***********************************************************************
.\CONTROLS\LISTCONTROLPACKAGE\GUILISTVIEWITEMTEMPLATES.CPP
***********************************************************************/

namespace vl
{
	namespace presentation
	{
		namespace controls
		{
			using namespace elements;
			using namespace compositions;
			using namespace collections;
			using namespace reflection::description;

			namespace list
			{

/***********************************************************************
DefaultListViewItemTemplate
***********************************************************************/

				DefaultListViewItemTemplate::DefaultListViewItemTemplate()
				{
					SetMinSizeLimitation(GuiGraphicsComposition::LimitToElementAndChildren);
				}

				DefaultListViewItemTemplate::~DefaultListViewItemTemplate()
				{
				}

/***********************************************************************
BigIconListViewItemTemplate
***********************************************************************/

				void BigIconListViewItemTemplate::OnInitialize()
				{
					DefaultListViewItemTemplate::OnInitialize();
					{
						auto table = new GuiTableComposition;
						AddChild(table);
						table->SetRowsAndColumns(2, 3);
						table->SetRowOption(0, GuiCellOption::MinSizeOption());
						table->SetRowOption(1, GuiCellOption::MinSizeOption());
						table->SetColumnOption(0, GuiCellOption::PercentageOption(0.5));
						table->SetColumnOption(1, GuiCellOption::MinSizeOption());
						table->SetColumnOption(2, GuiCellOption::PercentageOption(0.5));
						table->SetAlignmentToParent(Margin(0, 0, 0, 0));
						table->SetCellPadding(5);
						{
							auto cell = new GuiCellComposition;
							table->AddChild(cell);
							cell->SetSite(0, 1, 1, 1);
							cell->SetPreferredMinSize(Size(32, 32));

							image = GuiImageFrameElement::Create();
							image->SetStretch(true);
							cell->SetOwnedElement(image);
						}
						{
							auto cell = new GuiCellComposition;
							table->AddChild(cell);
							cell->SetMinSizeLimitation(GuiGraphicsComposition::NoLimit);
							cell->SetSite(1, 0, 1, 3);
							cell->SetPreferredMinSize(Size(64, 40));

							text = GuiSolidLabelElement::Create();
							text->SetAlignments(Alignment::Center, Alignment::Top);
							text->SetWrapLine(true);
							text->SetEllipse(true);
							cell->SetOwnedElement(text);
						}
					}

					if (auto listView = dynamic_cast<GuiVirtualListView*>(listControl))
					{
						auto itemIndex = GetIndex();
						if (auto view = dynamic_cast<IListViewItemView*>(listView->GetItemProvider()->RequestView(IListViewItemView::Identifier)))
						{
							auto imageData = view->GetLargeImage(itemIndex);
							if (imageData)
							{
								image->SetImage(imageData->GetImage(), imageData->GetFrameIndex());
							}
							else
							{
								image->SetImage(nullptr);
							}
							text->SetText(view->GetText(itemIndex));
							text->SetColor(listView->GetControlTemplateObject(true)->GetPrimaryTextColor());
						}
					}

					FontChanged.AttachMethod(this, &BigIconListViewItemTemplate::OnFontChanged);

					FontChanged.Execute(compositions::GuiEventArgs(this));
				}

				void BigIconListViewItemTemplate::OnFontChanged(compositions::GuiGraphicsComposition* sender, compositions::GuiEventArgs& arguments)
				{
					text->SetFont(GetFont());
				}

				BigIconListViewItemTemplate::BigIconListViewItemTemplate()
				{
				}

				BigIconListViewItemTemplate::~BigIconListViewItemTemplate()
				{
				}

/***********************************************************************
SmallIconListViewItemTemplate
***********************************************************************/

				void SmallIconListViewItemTemplate::OnInitialize()
				{
					DefaultListViewItemTemplate::OnInitialize();
					{
						auto table = new GuiTableComposition;
						AddChild(table);
						table->SetRowsAndColumns(3, 2);
						table->SetRowOption(0, GuiCellOption::PercentageOption(0.5));
						table->SetRowOption(1, GuiCellOption::MinSizeOption());
						table->SetRowOption(2, GuiCellOption::PercentageOption(0.5));
						table->SetColumnOption(0, GuiCellOption::MinSizeOption());
						table->SetColumnOption(1, GuiCellOption::MinSizeOption());
						table->SetAlignmentToParent(Margin(0, 0, 0, 0));
						table->SetCellPadding(2);
						{
							GuiCellComposition* cell = new GuiCellComposition;
							table->AddChild(cell);
							cell->SetSite(1, 0, 1, 1);
							cell->SetPreferredMinSize(Size(16, 16));

							image = GuiImageFrameElement::Create();
							image->SetStretch(true);
							cell->SetOwnedElement(image);
						}
						{
							GuiCellComposition* cell = new GuiCellComposition;
							table->AddChild(cell);
							cell->SetSite(0, 1, 3, 1);
							cell->SetPreferredMinSize(Size(192, 0));

							text = GuiSolidLabelElement::Create();
							text->SetAlignments(Alignment::Left, Alignment::Center);
							text->SetEllipse(true);
							cell->SetOwnedElement(text);
						}
					}

					if (auto listView = dynamic_cast<GuiVirtualListView*>(listControl))
					{
						auto itemIndex = GetIndex();
						if (auto view = dynamic_cast<IListViewItemView*>(listView->GetItemProvider()->RequestView(IListViewItemView::Identifier)))
						{
							auto imageData = view->GetSmallImage(itemIndex);
							if (imageData)
							{
								image->SetImage(imageData->GetImage(), imageData->GetFrameIndex());
							}
							else
							{
								image->SetImage(nullptr);
							}
							text->SetText(view->GetText(itemIndex));
							text->SetColor(listView->GetControlTemplateObject(true)->GetPrimaryTextColor());
						}
					}

					FontChanged.AttachMethod(this, &SmallIconListViewItemTemplate::OnFontChanged);

					FontChanged.Execute(compositions::GuiEventArgs(this));
				}

				void SmallIconListViewItemTemplate::OnFontChanged(compositions::GuiGraphicsComposition* sender, compositions::GuiEventArgs& arguments)
				{
					text->SetFont(GetFont());
				}

				SmallIconListViewItemTemplate::SmallIconListViewItemTemplate()
				{
				}

				SmallIconListViewItemTemplate::~SmallIconListViewItemTemplate()
				{
				}

/***********************************************************************
ListListViewItemTemplate
***********************************************************************/

				void ListListViewItemTemplate::OnInitialize()
				{
					DefaultListViewItemTemplate::OnInitialize();
					{
						auto table = new GuiTableComposition;
						AddChild(table);
						table->SetRowsAndColumns(3, 2);
						table->SetRowOption(0, GuiCellOption::PercentageOption(0.5));
						table->SetRowOption(1, GuiCellOption::MinSizeOption());
						table->SetRowOption(2, GuiCellOption::PercentageOption(0.5));
						table->SetColumnOption(0, GuiCellOption::MinSizeOption());
						table->SetColumnOption(1, GuiCellOption::MinSizeOption());
						table->SetAlignmentToParent(Margin(0, 0, 0, 0));
						table->SetCellPadding(2);
						{
							auto cell = new GuiCellComposition;
							table->AddChild(cell);
							cell->SetSite(1, 0, 1, 1);
							cell->SetPreferredMinSize(Size(16, 16));

							image = GuiImageFrameElement::Create();
							image->SetStretch(true);
							cell->SetOwnedElement(image);
						}
						{
							auto cell = new GuiCellComposition;
							table->AddChild(cell);
							cell->SetSite(0, 1, 3, 1);
							cell->SetMargin(Margin(0, 0, 16, 0));

							text = GuiSolidLabelElement::Create();
							text->SetAlignments(Alignment::Left, Alignment::Center);
							cell->SetOwnedElement(text);
						}
					}

					if (auto listView = dynamic_cast<GuiVirtualListView*>(listControl))
					{
						auto itemIndex = GetIndex();
						if (auto view = dynamic_cast<IListViewItemView*>(listView->GetItemProvider()->RequestView(IListViewItemView::Identifier)))
						{
							auto imageData = view->GetSmallImage(itemIndex);
							if (imageData)
							{
								image->SetImage(imageData->GetImage(), imageData->GetFrameIndex());
							}
							else
							{
								image->SetImage(nullptr);
							}
							text->SetText(view->GetText(itemIndex));
							text->SetColor(listView->GetControlTemplateObject(true)->GetPrimaryTextColor());
						}
					}

					FontChanged.AttachMethod(this, &ListListViewItemTemplate::OnFontChanged);

					FontChanged.Execute(compositions::GuiEventArgs(this));
				}

				void ListListViewItemTemplate::OnFontChanged(compositions::GuiGraphicsComposition* sender, compositions::GuiEventArgs& arguments)
				{
					text->SetFont(GetFont());
				}

				ListListViewItemTemplate::ListListViewItemTemplate()
				{
				}

				ListListViewItemTemplate::~ListListViewItemTemplate()
				{
				}

/***********************************************************************
TileListViewItemTemplate
***********************************************************************/

				elements::GuiSolidLabelElement* TileListViewItemTemplate::CreateTextElement(vint textRow)
				{
					auto cell = new GuiCellComposition;
					textTable->AddChild(cell);
					cell->SetSite(textRow + 1, 0, 1, 1);

					auto textElement = GuiSolidLabelElement::Create();
					textElement->SetAlignments(Alignment::Left, Alignment::Center);
					textElement->SetEllipse(true);
					cell->SetOwnedElement(textElement);
					return textElement;
				}

				void TileListViewItemTemplate::ResetTextTable(vint textRows)
				{
					textTable->SetRowsAndColumns(textRows + 2, 1);
					textTable->SetRowOption(0, GuiCellOption::PercentageOption(0.5));
					for (vint i = 0; i<textRows; i++)
					{
						textTable->SetRowOption(i + 1, GuiCellOption::MinSizeOption());
					}
					textTable->SetRowOption(textRows + 1, GuiCellOption::PercentageOption(0.5));
					textTable->SetColumnOption(0, GuiCellOption::PercentageOption(1.0));
				}

				void TileListViewItemTemplate::OnInitialize()
				{
					DefaultListViewItemTemplate::OnInitialize();
					{
						auto table = new GuiTableComposition;
						AddChild(table);
						table->SetMinSizeLimitation(GuiGraphicsComposition::LimitToElementAndChildren);
						table->SetRowsAndColumns(3, 2);
						table->SetRowOption(0, GuiCellOption::PercentageOption(0.5));
						table->SetRowOption(1, GuiCellOption::MinSizeOption());
						table->SetRowOption(2, GuiCellOption::PercentageOption(0.5));
						table->SetColumnOption(0, GuiCellOption::MinSizeOption());
						table->SetColumnOption(1, GuiCellOption::MinSizeOption());
						table->SetAlignmentToParent(Margin(0, 0, 0, 0));
						table->SetCellPadding(4);
						{
							auto cell = new GuiCellComposition;
							table->AddChild(cell);
							cell->SetSite(1, 0, 1, 1);
							cell->SetPreferredMinSize(Size(32, 32));

							image = GuiImageFrameElement::Create();
							image->SetStretch(true);
							cell->SetOwnedElement(image);
						}
						{
							auto cell = new GuiCellComposition;
							table->AddChild(cell);
							cell->SetSite(0, 1, 3, 1);
							cell->SetPreferredMinSize(Size(224, 0));

							textTable = new GuiTableComposition;
							textTable->SetMinSizeLimitation(GuiGraphicsComposition::LimitToElementAndChildren);
							textTable->SetCellPadding(1);
							ResetTextTable(1);
							textTable->SetAlignmentToParent(Margin(0, 0, 0, 0));
							cell->AddChild(textTable);
							{
								text = CreateTextElement(0);
							}
						}
					}

					if (auto listView = dynamic_cast<GuiVirtualListView*>(listControl))
					{
						auto itemIndex = GetIndex();
						if (auto view = dynamic_cast<IListViewItemView*>(listView->GetItemProvider()->RequestView(IListViewItemView::Identifier)))
						{
							auto imageData = view->GetLargeImage(itemIndex);
							if (imageData)
							{
								image->SetImage(imageData->GetImage(), imageData->GetFrameIndex());
							}
							else
							{
								image->SetImage(nullptr);
							}
							text->SetText(view->GetText(itemIndex));
							text->SetColor(listView->GetControlTemplateObject(true)->GetPrimaryTextColor());

							vint dataColumnCount = view->GetDataColumnCount();
							ResetTextTable(dataColumnCount + 1);
							dataTexts.Resize(dataColumnCount);
							for (vint i = 0; i < dataColumnCount; i++)
							{
								dataTexts[i] = CreateTextElement(i + 1);
								dataTexts[i]->SetText(view->GetSubItem(itemIndex, view->GetDataColumn(i)));
								dataTexts[i]->SetColor(listView->GetControlTemplateObject(true)->GetSecondaryTextColor());
							}
						}
					}

					FontChanged.AttachMethod(this, &TileListViewItemTemplate::OnFontChanged);

					FontChanged.Execute(compositions::GuiEventArgs(this));
				}

				void TileListViewItemTemplate::OnFontChanged(compositions::GuiGraphicsComposition* sender, compositions::GuiEventArgs& arguments)
				{
					text->SetFont(GetFont());
					if (auto view = dynamic_cast<IListViewItemView*>(listControl->GetItemProvider()->RequestView(IListViewItemView::Identifier)))
					{
						vint dataColumnCount = view->GetDataColumnCount();
						for (vint i = 0; i < dataColumnCount; i++)
						{
							dataTexts[i]->SetFont(GetFont());
						}
					}
				}

				TileListViewItemTemplate::TileListViewItemTemplate()
				{
				}

				TileListViewItemTemplate::~TileListViewItemTemplate()
				{
				}

/***********************************************************************
InformationListViewItemTemplate
***********************************************************************/

				void InformationListViewItemTemplate::OnInitialize()
				{
					DefaultListViewItemTemplate::OnInitialize();
					{
						bottomLine = GuiSolidBackgroundElement::Create();
						bottomLineComposition = new GuiBoundsComposition;
						bottomLineComposition->SetOwnedElement(bottomLine);
						bottomLineComposition->SetAlignmentToParent(Margin(8, -1, 8, 0));
						bottomLineComposition->SetPreferredMinSize(Size(0, 1));
						AddChild(bottomLineComposition);

						auto table = new GuiTableComposition;
						AddChild(table);
						table->SetRowsAndColumns(3, 3);
						table->SetRowOption(0, GuiCellOption::PercentageOption(0.5));
						table->SetRowOption(1, GuiCellOption::MinSizeOption());
						table->SetRowOption(2, GuiCellOption::PercentageOption(0.5));
						table->SetColumnOption(0, GuiCellOption::MinSizeOption());
						table->SetColumnOption(1, GuiCellOption::PercentageOption(1.0));
						table->SetColumnOption(2, GuiCellOption::MinSizeOption());
						table->SetMinSizeLimitation(GuiGraphicsComposition::LimitToElementAndChildren);
						table->SetAlignmentToParent(Margin(0, 0, 0, 0));
						table->SetCellPadding(4);
						{
							auto cell = new GuiCellComposition;
							table->AddChild(cell);
							cell->SetSite(1, 0, 1, 1);
							cell->SetPreferredMinSize(Size(32, 32));

							image = GuiImageFrameElement::Create();
							image->SetStretch(true);
							cell->SetOwnedElement(image);
						}
						{
							auto cell = new GuiCellComposition;
							table->AddChild(cell);
							cell->SetSite(0, 1, 3, 1);

							text = GuiSolidLabelElement::Create();
							text->SetEllipse(true);
							cell->SetOwnedElement(text);
						}
						{
							auto cell = new GuiCellComposition;
							table->AddChild(cell);
							cell->SetSite(0, 2, 3, 1);
							cell->SetPreferredMinSize(Size(224, 0));

							textTable = new GuiTableComposition;
							textTable->SetCellPadding(4);
							textTable->SetMinSizeLimitation(GuiGraphicsComposition::LimitToElementAndChildren);
							textTable->SetAlignmentToParent(Margin(0, 0, 0, 0));
							cell->AddChild(textTable);
						}
					}

					if (auto listView = dynamic_cast<GuiVirtualListView*>(listControl))
					{
						auto itemIndex = GetIndex();
						if (auto view = dynamic_cast<IListViewItemView*>(listView->GetItemProvider()->RequestView(IListViewItemView::Identifier)))
						{
							auto imageData = view->GetLargeImage(itemIndex);
							if (imageData)
							{
								image->SetImage(imageData->GetImage(), imageData->GetFrameIndex());
							}
							else
							{
								image->SetImage(nullptr);
							}
							text->SetText(view->GetText(itemIndex));
							text->SetColor(listView->GetControlTemplateObject(true)->GetPrimaryTextColor());
							bottomLine->SetColor(listView->GetControlTemplateObject(true)->GetItemSeparatorColor());

							vint dataColumnCount = view->GetDataColumnCount();
							columnTexts.Resize(dataColumnCount);
							dataTexts.Resize(dataColumnCount);

							textTable->SetRowsAndColumns(dataColumnCount + 2, 1);
							textTable->SetRowOption(0, GuiCellOption::PercentageOption(0.5));
							for (vint i = 0; i < dataColumnCount; i++)
							{
								textTable->SetRowOption(i + 1, GuiCellOption::MinSizeOption());
							}
							textTable->SetRowOption(dataColumnCount + 1, GuiCellOption::PercentageOption(0.5));
							textTable->SetColumnOption(0, GuiCellOption::PercentageOption(1.0));

							for (vint i = 0; i < dataColumnCount; i++)
							{
								auto cell = new GuiCellComposition;
								textTable->AddChild(cell);
								cell->SetSite(i + 1, 0, 1, 1);

								auto dataTable = new GuiTableComposition;
								dataTable->SetRowsAndColumns(1, 2);
								dataTable->SetRowOption(0, GuiCellOption::MinSizeOption());
								dataTable->SetColumnOption(0, GuiCellOption::MinSizeOption());
								dataTable->SetColumnOption(1, GuiCellOption::PercentageOption(1.0));
								dataTable->SetMinSizeLimitation(GuiGraphicsComposition::LimitToElementAndChildren);
								dataTable->SetAlignmentToParent(Margin(0, 0, 0, 0));
								cell->AddChild(dataTable);
								{
									auto cell = new GuiCellComposition;
									dataTable->AddChild(cell);
									cell->SetSite(0, 0, 1, 1);

									columnTexts[i] = GuiSolidLabelElement::Create();
									columnTexts[i]->SetText(view->GetColumnText(view->GetDataColumn(i) + 1) + L": ");
									columnTexts[i]->SetColor(listView->GetControlTemplateObject(true)->GetSecondaryTextColor());
									cell->SetOwnedElement(columnTexts[i]);
								}
								{
									auto cell = new GuiCellComposition;
									dataTable->AddChild(cell);
									cell->SetSite(0, 1, 1, 1);

									dataTexts[i]= GuiSolidLabelElement::Create();
									dataTexts[i]->SetEllipse(true);
									dataTexts[i]->SetText(view->GetSubItem(itemIndex, view->GetDataColumn(i)));
									dataTexts[i]->SetColor(listView->GetControlTemplateObject(true)->GetPrimaryTextColor());
									cell->SetOwnedElement(dataTexts[i]);
								}
							}
						}
					}

					FontChanged.AttachMethod(this, &InformationListViewItemTemplate::OnFontChanged);

					FontChanged.Execute(compositions::GuiEventArgs(this));
				}

				void InformationListViewItemTemplate::OnFontChanged(compositions::GuiGraphicsComposition* sender, compositions::GuiEventArgs& arguments)
				{
					{
						auto font = GetFont();
						font.size = (vint)(font.size * 1.2);
						text->SetFont(font);
					}
					if (auto view = dynamic_cast<IListViewItemView*>(listControl->GetItemProvider()->RequestView(IListViewItemView::Identifier)))
					{
						vint dataColumnCount = view->GetDataColumnCount();
						for (vint i = 0; i < dataColumnCount; i++)
						{
							columnTexts[i]->SetFont(GetFont());
							dataTexts[i]->SetFont(GetFont());
						}
					}
				}

				InformationListViewItemTemplate::InformationListViewItemTemplate()
				{
				}

				InformationListViewItemTemplate::~InformationListViewItemTemplate()
				{
				}

/***********************************************************************
DetailListViewItemTemplate
***********************************************************************/

				void DetailListViewItemTemplate::OnInitialize()
				{
					DefaultListViewItemTemplate::OnInitialize();
					columnItemView = dynamic_cast<ListViewColumnItemArranger::IColumnItemView*>(listControl->GetItemProvider()->RequestView(ListViewColumnItemArranger::IColumnItemView::Identifier));

					{
						textTable = new GuiTableComposition;
						textTable->SetAlignmentToParent(Margin(0, 0, 0, 0));
						textTable->SetRowsAndColumns(1, 1);
						textTable->SetRowOption(0, GuiCellOption::MinSizeOption());
						textTable->SetColumnOption(0, GuiCellOption::AbsoluteOption(0));
						AddChild(textTable);
						{
							auto cell = new GuiCellComposition;
							textTable->AddChild(cell);
							cell->SetSite(0, 0, 1, 1);

							auto table = new GuiTableComposition;
							cell->AddChild(table);
							table->SetRowsAndColumns(3, 2);
							table->SetRowOption(0, GuiCellOption::PercentageOption(0.5));
							table->SetRowOption(1, GuiCellOption::MinSizeOption());
							table->SetRowOption(2, GuiCellOption::PercentageOption(0.5));
							table->SetColumnOption(0, GuiCellOption::MinSizeOption());
							table->SetColumnOption(1, GuiCellOption::PercentageOption(1.0));
							table->SetAlignmentToParent(Margin(0, 0, 0, 0));
							table->SetCellPadding(2);
							{
								auto cell = new GuiCellComposition;
								table->AddChild(cell);
								cell->SetSite(1, 0, 1, 1);
								cell->SetPreferredMinSize(Size(16, 16));

								image = GuiImageFrameElement::Create();
								image->SetStretch(true);
								cell->SetOwnedElement(image);
							}
							{
								auto cell = new GuiCellComposition;
								table->AddChild(cell);
								cell->SetSite(0, 1, 3, 1);
								cell->SetMargin(Margin(0, 0, 8, 0));

								text = GuiSolidLabelElement::Create();
								text->SetAlignments(Alignment::Left, Alignment::Center);
								text->SetEllipse(true);
								cell->SetOwnedElement(text);
							}
						}
					}

					if (auto listView = dynamic_cast<GuiVirtualListView*>(listControl))
					{
						auto itemIndex = GetIndex();
						if (auto view = dynamic_cast<IListViewItemView*>(listView->GetItemProvider()->RequestView(IListViewItemView::Identifier)))
						{
							auto imageData = view->GetSmallImage(itemIndex);
							if (imageData)
							{
								image->SetImage(imageData->GetImage(), imageData->GetFrameIndex());
							}
							else
							{
								image->SetImage(0);
							}
							text->SetText(view->GetText(itemIndex));
							text->SetColor(listView->GetControlTemplateObject(true)->GetPrimaryTextColor());

							vint columnCount = view->GetColumnCount() - 1;
							subItems.Resize(columnCount);
							textTable->SetRowsAndColumns(1, columnCount + 1);
							for (vint i = 0; i < columnCount; i++)
							{
								auto cell = new GuiCellComposition;
								textTable->AddChild(cell);
								cell->SetSite(0, i + 1, 1, 1);
								cell->SetMargin(Margin(8, 0, 8, 0));

								subItems[i] = GuiSolidLabelElement::Create();
								subItems[i]->SetAlignments(Alignment::Left, Alignment::Center);
								subItems[i]->SetFont(text->GetFont());
								subItems[i]->SetEllipse(true);
								subItems[i]->SetText(view->GetSubItem(itemIndex, i));
								subItems[i]->SetColor(listView->GetControlTemplateObject(true)->GetSecondaryTextColor());
								cell->SetOwnedElement(subItems[i]);
							}
							OnColumnChanged();
						}
					}

					FontChanged.AttachMethod(this, &DetailListViewItemTemplate::OnFontChanged);

					FontChanged.Execute(compositions::GuiEventArgs(this));
				}

				void DetailListViewItemTemplate::OnColumnChanged()
				{
					if (auto view = dynamic_cast<IListViewItemView*>(listControl->GetItemProvider()->RequestView(IListViewItemView::Identifier)))
					{
						if (columnItemView)
						{
							vint columnCount = view->GetColumnCount();
							if (columnCount>textTable->GetColumns())
							{
								columnCount = textTable->GetColumns();
							}
							for (vint i = 0; i<columnCount; i++)
							{
								textTable->SetColumnOption(i, GuiCellOption::AbsoluteOption(columnItemView->GetColumnSize(i)));
							}
							textTable->UpdateCellBounds();
						}
					}
				}

				void DetailListViewItemTemplate::OnFontChanged(compositions::GuiGraphicsComposition* sender, compositions::GuiEventArgs& arguments)
				{
					text->SetFont(GetFont());
					if (auto view = dynamic_cast<IListViewItemView*>(listControl->GetItemProvider()->RequestView(IListViewItemView::Identifier)))
					{
						vint columnCount = view->GetColumnCount() - 1;
						for (vint i = 0; i < columnCount; i++)
						{
							subItems[i]->SetFont(GetFont());
						}
					}
				}

				DetailListViewItemTemplate::DetailListViewItemTemplate()
				{
				}

				DetailListViewItemTemplate::~DetailListViewItemTemplate()
				{
				}
			}
		}
	}
}

/***********************************************************************
.\CONTROLS\TEXTEDITORPACKAGE\EDITORCALLBACK\GUITEXTGENERALOPERATIONS.CPP
***********************************************************************/

namespace vl
{
	namespace presentation
	{
		namespace controls
		{
		}
	}
}

/***********************************************************************
.\CONTROLS\TEXTEDITORPACKAGE\EDITORCALLBACK\GUITEXTCOLORIZER.CPP
***********************************************************************/

namespace vl
{
	namespace presentation
	{
		namespace controls
		{
			using namespace elements;
			using namespace elements::text;

/***********************************************************************
GuiTextBoxColorizerBase
***********************************************************************/

			void GuiTextBoxColorizerBase::ColorizerThreadProc(void* argument)
			{
				GuiTextBoxColorizerBase* colorizer=(GuiTextBoxColorizerBase*)argument;
				while(!colorizer->isFinalizing)
				{
					vint lineIndex=-1;
					wchar_t* text=0;
					vuint32_t* colors=0;
					vint length=0;
					vint lexerState=-1;
					vint contextState=-1;

					SPIN_LOCK(*colorizer->elementModifyLock)
					{
						if(colorizer->colorizedLineCount>=colorizer->element->GetLines().GetCount())
						{
							colorizer->isColorizerRunning=false;
							goto CANCEL_COLORIZING;
						}

						lineIndex=colorizer->colorizedLineCount++;
						TextLine& line=colorizer->element->GetLines().GetLine(lineIndex);
						length=line.dataLength;
						text=new wchar_t[length+2];
						colors=new vuint32_t[length+2];
						memcpy(text, line.text, sizeof(wchar_t)*length);
						text[length]=L'\r';
						text[length+1]=L'\n';
						lexerState=lineIndex==0?colorizer->GetLexerStartState():colorizer->element->GetLines().GetLine(lineIndex-1).lexerFinalState;
						contextState=lineIndex==0?colorizer->GetContextStartState():colorizer->element->GetLines().GetLine(lineIndex-1).contextFinalState;
					}

					colorizer->ColorizeLineWithCRLF(lineIndex, text, colors, length+2, lexerState, contextState);

					SPIN_LOCK(*colorizer->elementModifyLock)
					{
						if(lineIndex<colorizer->colorizedLineCount && lineIndex<colorizer->element->GetLines().GetCount())
						{
							TextLine& line=colorizer->element->GetLines().GetLine(lineIndex);
							line.lexerFinalState=lexerState;
							line.contextFinalState=contextState;
							for(vint i=0;i<length;i++)
							{
								line.att[i].colorIndex=colors[i];
							}
						}
						delete[] text;
						delete[] colors;
					}
				}
			CANCEL_COLORIZING:
				colorizer->colorizerRunningEvent.Leave();
			}

			void GuiTextBoxColorizerBase::StartColorizer()
			{
				if(!isColorizerRunning)
				{
					isColorizerRunning=true;
					colorizerRunningEvent.Enter();
					ThreadPoolLite::Queue(&GuiTextBoxColorizerBase::ColorizerThreadProc, this);
				}
			}

			void GuiTextBoxColorizerBase::StopColorizer(bool forever)
			{
				isFinalizing=true;
				colorizerRunningEvent.Enter();
				colorizerRunningEvent.Leave();
				colorizedLineCount=0;
				if(!forever)
				{
					isFinalizing=false;
				}
			}

			void GuiTextBoxColorizerBase::StopColorizerForever()
			{
				StopColorizer(true);
			}

			GuiTextBoxColorizerBase::GuiTextBoxColorizerBase()
				:element(0)
				,elementModifyLock(0)
				,colorizedLineCount(0)
				,isColorizerRunning(false)
				,isFinalizing(false)
			{
			}

			GuiTextBoxColorizerBase::~GuiTextBoxColorizerBase()
			{
				StopColorizerForever();
			}

			void GuiTextBoxColorizerBase::Attach(elements::GuiColorizedTextElement* _element, SpinLock& _elementModifyLock, compositions::GuiGraphicsComposition* _ownerComposition, vuint editVersion)
			{
				if(_element)
				{
					SPIN_LOCK(_elementModifyLock)
					{
						element=_element;
						elementModifyLock=&_elementModifyLock;
						StartColorizer();
					}
				}
			}

			void GuiTextBoxColorizerBase::Detach()
			{
				if(element && elementModifyLock)
				{
					StopColorizer(false);
					SPIN_LOCK(*elementModifyLock)
					{
						element=0;
						elementModifyLock=0;
					}
				}
			}

			void GuiTextBoxColorizerBase::TextEditPreview(TextEditPreviewStruct& arguments)
			{
			}

			void GuiTextBoxColorizerBase::TextEditNotify(const TextEditNotifyStruct& arguments)
			{
				if(element && elementModifyLock)
				{
					SPIN_LOCK(*elementModifyLock)
					{
						vint line
							=arguments.originalStart.row<arguments.originalEnd.row
							?arguments.originalStart.row
							:arguments.originalEnd.row;
						if(colorizedLineCount>line)
						{
							colorizedLineCount=line;
						}
						StartColorizer();
					}
				}
			}

			void GuiTextBoxColorizerBase::TextCaretChanged(const TextCaretChangedStruct& arguments)
			{
			}

			void GuiTextBoxColorizerBase::TextEditFinished(vuint editVersion)
			{
			}

			void GuiTextBoxColorizerBase::RestartColorizer()
			{
				if(element && elementModifyLock)
				{
					SPIN_LOCK(*elementModifyLock)
					{
						colorizedLineCount=0;
						StartColorizer();
					}
				}
			}

/***********************************************************************
GuiTextBoxRegexColorizer
***********************************************************************/

			struct GuiTextBoxRegexColorizerProcData
			{
				GuiTextBoxRegexColorizer*		colorizer;
				vint							lineIndex;
				const wchar_t*					text;
				vuint32_t*						colors;
				vint							contextState;
			};

			void GuiTextBoxRegexColorizer::ColorizerProc(void* argument, vint start, vint length, vint token)
			{
				GuiTextBoxRegexColorizerProcData& data=*(GuiTextBoxRegexColorizerProcData*)argument;
				data.colorizer->ColorizeTokenContextSensitive(data.lineIndex, data.text, start, length, token, data.contextState);
				for(vint i=0;i<length;i++)
				{
					data.colors[start+i]=(int)token+1;
				}
			}

			GuiTextBoxRegexColorizer::GuiTextBoxRegexColorizer()
			{
				colors.Resize(1);
			}

			GuiTextBoxRegexColorizer::~GuiTextBoxRegexColorizer()
			{
				StopColorizerForever();
			}

			elements::text::ColorEntry GuiTextBoxRegexColorizer::GetDefaultColor()
			{
				return defaultColor;
			}

			collections::List<WString>& GuiTextBoxRegexColorizer::GetTokenRegexes()
			{
				return tokenRegexes;
			}

			collections::List<elements::text::ColorEntry>& GuiTextBoxRegexColorizer::GetTokenColors()
			{
				return tokenColors;
			}

			collections::List<elements::text::ColorEntry>& GuiTextBoxRegexColorizer::GetExtraTokenColors()
			{
				return extraTokenColors;
			}

			vint GuiTextBoxRegexColorizer::GetExtraTokenIndexStart()
			{
				if(lexer)
				{
					return tokenColors.Count();
				}
				else
				{
					return -1;
				}
			}

			bool GuiTextBoxRegexColorizer::SetDefaultColor(elements::text::ColorEntry value)
			{
				if(lexer)
				{
					return false;
				}
				else
				{
					defaultColor=value;
					return true;
				}
			}

			vint GuiTextBoxRegexColorizer::AddToken(const WString& regex, elements::text::ColorEntry color)
			{
				if(lexer)
				{
					return -1;
				}
				else
				{
					tokenRegexes.Add(regex);
					tokenColors.Add(color);
					return tokenColors.Count()-1;
				}
			}

			vint GuiTextBoxRegexColorizer::AddExtraToken(elements::text::ColorEntry color)
			{
				if(lexer)
				{
					return -1;
				}
				else
				{
					extraTokenColors.Add(color);
					return extraTokenColors.Count()-1;
				}
			}

			void GuiTextBoxRegexColorizer::ClearTokens()
			{
				tokenRegexes.Clear();
				tokenColors.Clear();
				extraTokenColors.Clear();
				lexer=0;
			}

			void GuiTextBoxRegexColorizer::Setup()
			{
				if(lexer || tokenRegexes.Count()==0)
				{
					colors.Resize(1);
					colors[0]=defaultColor;
				}
				else
				{
					lexer=new regex::RegexLexer(tokenRegexes);
					colors.Resize(1+tokenRegexes.Count()+extraTokenColors.Count());
					colors[0]=defaultColor;
					for(vint i=0;i<tokenColors.Count();i++)
					{
						colors[i+1]=tokenColors[i];
					}
					for(vint i=0;i<extraTokenColors.Count();i++)
					{
						colors[i+1+tokenColors.Count()]=extraTokenColors[i];
					}
					colorizer=new regex::RegexLexerColorizer(lexer->Colorize());
				}
			}

			void GuiTextBoxRegexColorizer::ColorizeTokenContextSensitive(vint lineIndex, const wchar_t* text, vint start, vint length, vint& token, vint& contextState)
			{
			}

			vint GuiTextBoxRegexColorizer::GetLexerStartState()
			{
				return lexer?colorizer->GetStartState():-1;
			}

			vint GuiTextBoxRegexColorizer::GetContextStartState()
			{
				return 0;
			}

			void GuiTextBoxRegexColorizer::ColorizeLineWithCRLF(vint lineIndex, const wchar_t* text, vuint32_t* colors, vint length, vint& lexerState, vint& contextState)
			{
				memset(colors, 0, sizeof(*colors)*length);
				if(lexer)
				{
					GuiTextBoxRegexColorizerProcData data;
					data.colorizer=this;
					data.lineIndex=lineIndex;
					data.text=text;
					data.colors=colors;
					data.contextState=contextState;

					colorizer->Reset(lexerState);
					colorizer->Colorize(text, length, &GuiTextBoxRegexColorizer::ColorizerProc, &data);

					lexerState=colorizer->GetCurrentState();
					contextState=data.contextState;
				}
				else
				{
					lexerState=-1;
					contextState=-1;
				}
			}

			const GuiTextBoxRegexColorizer::ColorArray& GuiTextBoxRegexColorizer::GetColors()
			{
				return colors;
			}
		}
	}
}

/***********************************************************************
.\CONTROLS\LISTCONTROLPACKAGE\GUIDATAGRIDCONTROLS.CPP
***********************************************************************/

namespace vl
{
	namespace presentation
	{
		namespace controls
		{
			namespace list
			{
				using namespace compositions;
				using namespace collections;
				using namespace description;
				using namespace templates;

				const wchar_t* const IDataGridView::Identifier = L"vl::presentation::controls::list::IDataGridView";

/***********************************************************************
DefaultDataGridItemTemplate
***********************************************************************/

				IDataVisualizerFactory* DefaultDataGridItemTemplate::GetDataVisualizerFactory(vint row, vint column)
				{
					if (auto dataGrid = dynamic_cast<GuiVirtualDataGrid*>(listControl))
					{
						if (auto factory = dataGrid->dataGridView->GetCellDataVisualizerFactory(row, column))
						{
							return factory;
						}

						if (column == 0)
						{
							return dataGrid->defaultMainColumnVisualizerFactory.Obj();
						}
						else
						{
							return dataGrid->defaultSubColumnVisualizerFactory.Obj();
						}

					}

					return nullptr;
				}

				IDataEditorFactory* DefaultDataGridItemTemplate::GetDataEditorFactory(vint row, vint column)
				{
					if (auto dataGrid = dynamic_cast<GuiVirtualDataGrid*>(listControl))
					{
						return dataGrid->dataGridView->GetCellDataEditorFactory(row, column);
					}
					return nullptr;
				}

				vint DefaultDataGridItemTemplate::GetCellColumnIndex(compositions::GuiGraphicsComposition* composition)
				{
					for (vint i = 0; i < textTable->GetColumns(); i++)
					{
						auto cell = textTable->GetSitedCell(0, i);
						if (composition == cell)
						{
							return i;
						}
					}
					return -1;
				}

				void DefaultDataGridItemTemplate::OnCellButtonUp(compositions::GuiGraphicsComposition* sender, bool openEditor)
				{
					if (auto dataGrid = dynamic_cast<GuiVirtualDataGrid*>(listControl))
					{
						vint index = GetCellColumnIndex(sender);
						if (index != -1)
						{
							if (currentEditor && dataGrid->GetSelectedCell().column == index)
							{
								return;
							}

							vint currentRow = GetIndex();
							dataGrid->StartEdit(currentRow, index);
						}
					}
				}

				bool DefaultDataGridItemTemplate::IsInEditor(compositions::GuiMouseEventArgs& arguments)
				{
					if (auto dataGrid = dynamic_cast<GuiVirtualDataGrid*>(listControl))
					{
						if (!dataGrid->currentEditor) return false;
						auto editorComposition = dataGrid->currentEditor->GetTemplate();
						auto currentComposition = arguments.eventSource;

						while (currentComposition)
						{
							if (currentComposition == editorComposition)
							{
								arguments.handled = true;
								return true;
							}
							else if (currentComposition == this)
							{
								break;
							}
							else
							{
								currentComposition = currentComposition->GetParent();
							}
						}

					}
					return false;
				}

				void DefaultDataGridItemTemplate::OnCellButtonDown(compositions::GuiGraphicsComposition* sender, compositions::GuiMouseEventArgs& arguments)
				{
					IsInEditor(arguments);
				}

				void DefaultDataGridItemTemplate::OnCellLeftButtonUp(compositions::GuiGraphicsComposition* sender, compositions::GuiMouseEventArgs& arguments)
				{
					if (auto dataGrid = dynamic_cast<GuiVirtualDataGrid*>(listControl))
					{
						if (!IsInEditor(arguments))
						{
							if (dataGrid->GetVisuallyEnabled())
							{
								OnCellButtonUp(sender, true);
							}
						}
					}
				}

				void DefaultDataGridItemTemplate::OnCellRightButtonUp(compositions::GuiGraphicsComposition* sender, compositions::GuiMouseEventArgs& arguments)
				{
					if (auto dataGrid = dynamic_cast<GuiVirtualDataGrid*>(listControl))
					{
						if (!IsInEditor(arguments))
						{
							if (dataGrid->GetVisuallyEnabled())
							{
								OnCellButtonUp(sender, false);
							}
						}
					}
				}

				void DefaultDataGridItemTemplate::OnColumnChanged()
				{
					UpdateSubItemSize();
				}

				void DefaultDataGridItemTemplate::OnInitialize()
				{
					DefaultListViewItemTemplate::OnInitialize();
					{
						textTable = new GuiTableComposition;
						textTable->SetAlignmentToParent(Margin(0, 0, 0, 0));
						textTable->SetRowsAndColumns(1, 1);
						textTable->SetRowOption(0, GuiCellOption::MinSizeOption());
						textTable->SetColumnOption(0, GuiCellOption::AbsoluteOption(0));
						AddChild(textTable);
					}

					if (auto dataGrid = dynamic_cast<GuiVirtualDataGrid*>(listControl))
					{
						vint columnCount = dataGrid->listViewItemView->GetColumnCount();
						vint itemIndex = GetIndex();

						dataVisualizers.Resize(columnCount);
						for (vint i = 0; i < dataVisualizers.Count(); i++)
						{
							auto factory = GetDataVisualizerFactory(itemIndex, i);
							dataVisualizers[i] = factory->CreateVisualizer(dataGrid);
						}

						textTable->SetRowsAndColumns(1, columnCount);
						for (vint i = 0; i < columnCount; i++)
						{
							auto cell = new GuiCellComposition;
							textTable->AddChild(cell);
							cell->SetSite(0, i, 1, 1);
							cell->GetEventReceiver()->leftButtonDown.AttachMethod(this, &DefaultDataGridItemTemplate::OnCellButtonDown);
							cell->GetEventReceiver()->rightButtonDown.AttachMethod(this, &DefaultDataGridItemTemplate::OnCellButtonDown);
							cell->GetEventReceiver()->leftButtonUp.AttachMethod(this, &DefaultDataGridItemTemplate::OnCellLeftButtonUp);
							cell->GetEventReceiver()->rightButtonUp.AttachMethod(this, &DefaultDataGridItemTemplate::OnCellRightButtonUp);

							auto composition = dataVisualizers[i]->GetTemplate();
							composition->SetAlignmentToParent(Margin(0, 0, 0, 0));
							cell->AddChild(composition);
						}

						for (vint i = 0; i < dataVisualizers.Count(); i++)
						{
							dataVisualizers[i]->BeforeVisualizeCell(dataGrid->GetItemProvider(), itemIndex, i);
						}

						GridPos selectedCell = dataGrid->GetSelectedCell();
						if (selectedCell.row == itemIndex)
						{
							NotifySelectCell(selectedCell.column);
						}
						else
						{
							NotifySelectCell(-1);
						}
						UpdateSubItemSize();
					}

					SelectedChanged.AttachMethod(this, &DefaultDataGridItemTemplate::OnSelectedChanged);
					FontChanged.AttachMethod(this, &DefaultDataGridItemTemplate::OnFontChanged);
					ContextChanged.AttachMethod(this, &DefaultDataGridItemTemplate::OnContextChanged);

					SelectedChanged.Execute(compositions::GuiEventArgs(this));
					FontChanged.Execute(compositions::GuiEventArgs(this));
					ContextChanged.Execute(compositions::GuiEventArgs(this));
				}

				void DefaultDataGridItemTemplate::OnSelectedChanged(compositions::GuiGraphicsComposition* sender, compositions::GuiEventArgs& arguments)
				{
					if (!GetSelected())
					{
						NotifySelectCell(-1);
					}
				}

				void DefaultDataGridItemTemplate::OnFontChanged(compositions::GuiGraphicsComposition* sender, compositions::GuiEventArgs& arguments)
				{
					FOREACH(Ptr<IDataVisualizer>, visualizer, dataVisualizers)
					{
						visualizer->GetTemplate()->SetFont(GetFont());
					}
					if (currentEditor)
					{
						currentEditor->GetTemplate()->SetFont(GetFont());
					}
				}

				void DefaultDataGridItemTemplate::OnContextChanged(compositions::GuiGraphicsComposition* sender, compositions::GuiEventArgs& arguments)
				{
					FOREACH(Ptr<IDataVisualizer>, visualizer, dataVisualizers)
					{
						visualizer->GetTemplate()->SetContext(GetContext());
					}
					if (currentEditor)
					{
						currentEditor->GetTemplate()->SetContext(GetContext());
					}
				}

				DefaultDataGridItemTemplate::DefaultDataGridItemTemplate()
				{
				}

				DefaultDataGridItemTemplate::~DefaultDataGridItemTemplate()
				{
					FOREACH(Ptr<IDataVisualizer>, visualizer, dataVisualizers)
					{
						visualizer->NotifyDeletedTemplate();
					}
					if (currentEditor)
					{
						currentEditor->NotifyDeletedTemplate();
					}
				}

				void DefaultDataGridItemTemplate::UpdateSubItemSize()
				{
					if (auto dataGrid = dynamic_cast<GuiVirtualDataGrid*>(listControl))
					{
						vint columnCount = dataGrid->listViewItemView->GetColumnCount();
						if (columnCount > textTable->GetColumns())
						{
							columnCount = textTable->GetColumns();
						}
						for (vint i = 0; i < columnCount; i++)
						{
							textTable->SetColumnOption(i, GuiCellOption::AbsoluteOption(dataGrid->columnItemView->GetColumnSize(i)));
						}
						textTable->UpdateCellBounds();
					}
				}

				bool DefaultDataGridItemTemplate::IsEditorOpened()
				{
					return currentEditor != nullptr;
				}

				void DefaultDataGridItemTemplate::NotifyOpenEditor(vint column, IDataEditor* editor)
				{
					currentEditor = editor;
					if (currentEditor)
					{
						auto cell = textTable->GetSitedCell(0, column);
						auto* editorBounds = currentEditor->GetTemplate();
						editorBounds->SetFont(GetFont());
						editorBounds->SetContext(GetContext());
						if (editorBounds->GetParent() && editorBounds->GetParent() != cell)
						{
							editorBounds->GetParent()->RemoveChild(editorBounds);
						}
						editorBounds->SetAlignmentToParent(Margin(0, 0, 0, 0));
						cell->AddChild(editorBounds);
						if (auto focusControl = currentEditor->GetTemplate()->GetFocusControl())
						{
							focusControl->SetFocus();
						}
					}
				}

				void DefaultDataGridItemTemplate::NotifyCloseEditor()
				{
					if (currentEditor)
					{
						auto composition = currentEditor->GetTemplate();
						if (composition->GetParent())
						{
							composition->GetParent()->RemoveChild(composition);
						}
						currentEditor = nullptr;
					}
				}

				void DefaultDataGridItemTemplate::NotifySelectCell(vint column)
				{
					for (vint i = 0; i < dataVisualizers.Count(); i++)
					{
						dataVisualizers[i]->SetSelected(i == column);
					}
				}

				void DefaultDataGridItemTemplate::NotifyCellEdited()
				{
					for (vint i = 0; i < dataVisualizers.Count(); i++)
					{
						dataVisualizers[i]->BeforeVisualizeCell(listControl->GetItemProvider(), GetIndex(), i);
					}
				}
			}
				
/***********************************************************************
GuiVirtualDataGrid (Editor)
***********************************************************************/

			using namespace list;

			void GuiVirtualDataGrid::OnItemModified(vint start, vint count, vint newCount)
			{
				GuiVirtualListView::OnItemModified(start, count, newCount);
				if(!GetItemProvider()->IsEditing())
				{
					StopEdit(false);
				}
			}

			void GuiVirtualDataGrid::OnStyleUninstalled(ItemStyle* style)
			{
				GuiVirtualListView::OnStyleUninstalled(style);
				if (auto itemStyle = dynamic_cast<DefaultDataGridItemTemplate*>(style))
				{
					if (itemStyle->IsEditorOpened())
					{
						itemStyle->NotifyCloseEditor();
						currentEditor = nullptr;
						currentEditorPos = { -1,-1 };
					}
				}
			}

			void GuiVirtualDataGrid::NotifyCloseEditor()
			{
				if (currentEditorPos.row != -1 && GetArranger())
				{
					auto style = GetArranger()->GetVisibleStyle(currentEditorPos.row);
					if (auto itemStyle = dynamic_cast<DefaultDataGridItemTemplate*>(style))
					{
						itemStyle->NotifyCloseEditor();
					}
				}
			}

			void GuiVirtualDataGrid::NotifySelectCell(vint row, vint column)
			{
				selectedCell = { row, column };
				SelectedCellChanged.Execute(GetNotifyEventArguments());

				auto style = GetArranger()->GetVisibleStyle(row);
				if (auto itemStyle = dynamic_cast<DefaultDataGridItemTemplate*>(style))
				{
					itemStyle->NotifySelectCell(column);
				}
			}

			bool GuiVirtualDataGrid::StartEdit(vint row, vint column)
			{
				StopEdit(true);
				NotifySelectCell(row, column);

				auto style = GetArranger()->GetVisibleStyle(row);
				if (auto itemStyle = dynamic_cast<DefaultDataGridItemTemplate*>(style))
				{
					if (auto factory = dataGridView->GetCellDataEditorFactory(row, column))
					{
						currentEditorOpeningEditor = true;
						currentEditorPos = { row,column };
						currentEditor = factory->CreateEditor(this);
						currentEditor->BeforeEditCell(GetItemProvider(), row, column);
						itemStyle->NotifyOpenEditor(column, currentEditor.Obj());
						currentEditorOpeningEditor = false;
						return true;
					}
				}
				return false;
			}

			void GuiVirtualDataGrid::StopEdit(bool forOpenNewEditor)
			{
				if (GetItemProvider()->IsEditing())
				{
					NotifyCloseEditor();
				}
				else
				{
					if (currentEditorPos != GridPos{-1, -1})
					{
						if (currentEditor)
						{
							NotifyCloseEditor();
						}
						if (!forOpenNewEditor)
						{
							NotifySelectCell(-1, -1);
						}
					}
				}
				currentEditor = nullptr;
				currentEditorPos = { -1,-1 };
			}

/***********************************************************************
GuiVirtualDataGrid (IDataGridContext)
***********************************************************************/

			templates::GuiListViewTemplate* GuiVirtualDataGrid::GetListViewControlTemplate()
			{
				return GetControlTemplateObject(true);
			}

			void GuiVirtualDataGrid::RequestSaveData()
			{
				if (currentEditor && !currentEditorOpeningEditor)
				{
					GuiControl* focusedControl = nullptr;
					if (auto controlHost = GetRelatedControlHost())
					{
						if (auto graphicsHost = controlHost->GetGraphicsHost())
						{
							if (auto focusComposition = graphicsHost->GetFocusedComposition())
							{
								focusedControl = focusComposition->GetRelatedControl();
							}
						}
					}

					GetItemProvider()->PushEditing();
					dataGridView->SetBindingCellValue(currentEditorPos.row, currentEditorPos.column, currentEditor->GetTemplate()->GetCellValue());
					GetItemProvider()->PopEditing();

					auto style = GetArranger()->GetVisibleStyle(currentEditorPos.row);
					if (auto itemStyle = dynamic_cast<DefaultDataGridItemTemplate*>(style))
					{
						itemStyle->NotifyCellEdited();
					}

					if (currentEditor && focusedControl)
					{
						focusedControl->SetFocus();
					}
				}
			}

/***********************************************************************
GuiVirtualDataGrid
***********************************************************************/

			void GuiVirtualDataGrid::OnColumnClicked(compositions::GuiGraphicsComposition* sender, compositions::GuiItemEventArgs& arguments)
			{
				if(dataGridView->IsColumnSortable(arguments.itemIndex))
				{
					switch(columnItemView->GetSortingState(arguments.itemIndex))
					{
					case ColumnSortingState::NotSorted:
						dataGridView->SortByColumn(arguments.itemIndex, true);
						break;
					case ColumnSortingState::Ascending:
						dataGridView->SortByColumn(arguments.itemIndex, false);
						break;
					case ColumnSortingState::Descending:
						dataGridView->SortByColumn(-1, false);
						break;
					}
				}
			}

			GuiVirtualDataGrid::GuiVirtualDataGrid(theme::ThemeName themeName, GuiListControl::IItemProvider* _itemProvider)
				:GuiVirtualListView(themeName, _itemProvider)
			{
				listViewItemView = dynamic_cast<IListViewItemView*>(_itemProvider->RequestView(IListViewItemView::Identifier));
				columnItemView = dynamic_cast<ListViewColumnItemArranger::IColumnItemView*>(_itemProvider->RequestView(ListViewColumnItemArranger::IColumnItemView::Identifier));
				dataGridView = dynamic_cast<IDataGridView*>(_itemProvider->RequestView(IDataGridView::Identifier));

				{
					auto mainProperty = [](const Value&) { return new MainColumnVisualizerTemplate; };
					auto subProperty = [](const Value&) { return new SubColumnVisualizerTemplate; };
					auto cellBorderProperty = [](const Value&) { return new CellBorderVisualizerTemplate; };

					auto mainFactory = MakePtr<DataVisualizerFactory>(mainProperty);
					auto subFactory = MakePtr<DataVisualizerFactory>(subProperty);
					defaultMainColumnVisualizerFactory = MakePtr<DataVisualizerFactory>(cellBorderProperty, mainFactory);
					defaultSubColumnVisualizerFactory = MakePtr<DataVisualizerFactory>(cellBorderProperty, subFactory);
				}

				CHECK_ERROR(listViewItemView != nullptr, L"GuiVirtualDataGrid::GuiVirtualDataGrid(IStyleController*, GuiListControl::IItemProvider*)#Missing IListViewItemView from item provider.");
				CHECK_ERROR(columnItemView != nullptr, L"GuiVirtualDataGrid::GuiVirtualDataGrid(IStyleController*, GuiListControl::IItemProvider*)#Missing ListViewColumnItemArranger::IColumnItemView from item provider.");
				CHECK_ERROR(dataGridView != nullptr, L"GuiVirtualDataGrid::GuiVirtualDataGrid(IStyleController*, GuiListControl::IItemProvider*)#Missing IDataGridView from item provider.");

				SetViewToDefault();
				ColumnClicked.AttachMethod(this, &GuiVirtualDataGrid::OnColumnClicked);
				SelectedCellChanged.SetAssociatedComposition(boundsComposition);
			}

			GuiVirtualDataGrid::~GuiVirtualDataGrid()
			{
			}

			GuiListControl::IItemProvider* GuiVirtualDataGrid::GetItemProvider()
			{
				return GuiVirtualListView::GetItemProvider();
			}

			void GuiVirtualDataGrid::SetViewToDefault()
			{
				SetStyleAndArranger(
					[](const Value&) { return new list::DefaultDataGridItemTemplate; },
					new list::ListViewColumnItemArranger
				);
			}

			GridPos GuiVirtualDataGrid::GetSelectedCell()
			{
				return selectedCell;
			}

			void GuiVirtualDataGrid::SetSelectedCell(const GridPos& value)
			{
				if (selectedCell == value)
				{
					return;
				}

				bool validPos = 0 <= value.row && value.row < GetItemProvider()->Count() && 0 <= value.column && value.column < listViewItemView->GetColumnCount();
				StopEdit(true);

				if (validPos)
				{
					NotifySelectCell(value.row, value.column);
				}
				else
				{
					NotifySelectCell(-1, -1);
				}

				if (validPos)
				{
					EnsureItemVisible(value.row);
					ClearSelection();
					SetSelected(value.row, true);
					StartEdit(value.row, value.column);
				}
				else
				{
					ClearSelection();
				}
			}
		}
	}
}

/***********************************************************************
.\CONTROLS\LISTCONTROLPACKAGE\GUIDATAGRIDEXTENSIONS.CPP
***********************************************************************/

namespace vl
{
	namespace presentation
	{
		namespace controls
		{
			namespace list
			{
				using namespace compositions;
				using namespace elements;
				using namespace theme;
				using namespace templates;
				
/***********************************************************************
DataVisualizerBase
***********************************************************************/

				DataVisualizerBase::DataVisualizerBase()
				{
				}

				DataVisualizerBase::~DataVisualizerBase()
				{
					if (visualizerTemplate)
					{
						SafeDeleteComposition(visualizerTemplate);
					}
				}

				IDataVisualizerFactory* DataVisualizerBase::GetFactory()
				{
					return factory;
				}

				templates::GuiGridVisualizerTemplate* DataVisualizerBase::GetTemplate()
				{
					return visualizerTemplate;
				}

				void DataVisualizerBase::NotifyDeletedTemplate()
				{
					visualizerTemplate = nullptr;
				}

				void DataVisualizerBase::BeforeVisualizeCell(GuiListControl::IItemProvider* itemProvider, vint row, vint column)
				{
					if (auto listViewItemView = dynamic_cast<IListViewItemView*>(dataGridContext->GetItemProvider()->RequestView(IListViewItemView::Identifier)))
					{
						auto style = dataGridContext->GetListViewControlTemplate();
						visualizerTemplate->SetPrimaryTextColor(style->GetPrimaryTextColor());
						visualizerTemplate->SetSecondaryTextColor(style->GetSecondaryTextColor());
						visualizerTemplate->SetItemSeparatorColor(style->GetItemSeparatorColor());

						visualizerTemplate->SetLargeImage(listViewItemView->GetLargeImage(row));
						visualizerTemplate->SetSmallImage(listViewItemView->GetSmallImage(row));
						visualizerTemplate->SetText(column == 0 ? listViewItemView->GetText(row) : listViewItemView->GetSubItem(row, column - 1));
					}
					if (auto dataGridView = dynamic_cast<IDataGridView*>(dataGridContext->GetItemProvider()->RequestView(IDataGridView::Identifier)))
					{
						visualizerTemplate->SetRowValue(itemProvider->GetBindingValue(row));
						visualizerTemplate->SetCellValue(dataGridView->GetBindingCellValue(row, column));
					}
				}

				void DataVisualizerBase::SetSelected(bool value)
				{
					if (visualizerTemplate)
					{
						visualizerTemplate->SetSelected(value);
					}
				}

/***********************************************************************
DataVisualizerFactory
***********************************************************************/

				DataVisualizerFactory::ItemTemplate* DataVisualizerFactory::CreateItemTemplate(controls::list::IDataGridContext* dataGridContext)
				{
					ItemTemplate* itemTemplate = templateFactory({});
					CHECK_ERROR(itemTemplate, L"DataVisualizerFactory::CreateItemTemplate(IDataGridContext*)#An instance of GuiGridEditorTemplate is expected.");
					if (decoratedFactory)
					{
						auto childTemplate = decoratedFactory->CreateItemTemplate(dataGridContext);
						childTemplate->SetAlignmentToParent(Margin(0, 0, 0, 0));
						itemTemplate->GetContainerComposition()->AddChild(childTemplate);

#define FORWARD_EVENT(NAME)\
						itemTemplate->NAME##Changed.AttachLambda([=](GuiGraphicsComposition* sender, GuiEventArgs& arguments)\
						{\
							childTemplate->Set##NAME(itemTemplate->Get##NAME());\
						});\

#define FORWARD_EVENT_IMPL(CLASS, TYPE, NAME, VALUE) FORWARD_EVENT(NAME)

						GuiTemplate_PROPERTIES(FORWARD_EVENT_IMPL)
						GuiControlTemplate_PROPERTIES(FORWARD_EVENT_IMPL)
						GuiGridCellTemplate_PROPERTIES(FORWARD_EVENT_IMPL)
						GuiGridVisualizerTemplate_PROPERTIES(FORWARD_EVENT_IMPL)

#undef FORWARD_EVENT_IMPL
#undef FORWARD_EVENT
					}
					return itemTemplate;
				}

				DataVisualizerFactory::DataVisualizerFactory(TemplateProperty<ItemTemplate> _templateFactory, Ptr<DataVisualizerFactory> _decoratedFactory)
					:templateFactory(_templateFactory)
					, decoratedFactory(_decoratedFactory)
				{
				}

				DataVisualizerFactory::~DataVisualizerFactory()
				{
				}

				Ptr<controls::list::IDataVisualizer> DataVisualizerFactory::CreateVisualizer(controls::list::IDataGridContext* dataGridContext)
				{
					auto dataVisualizer = MakePtr<DataVisualizerBase>();
					dataVisualizer->factory = this;
					dataVisualizer->dataGridContext = dataGridContext;
					dataVisualizer->visualizerTemplate = CreateItemTemplate(dataGridContext);

					return dataVisualizer;
				}
				
/***********************************************************************
DataEditorBase
***********************************************************************/

				void DataEditorBase::OnCellValueChanged(compositions::GuiGraphicsComposition* sender, compositions::GuiEventArgs& arguments)
				{
					dataGridContext->RequestSaveData();
				}

				DataEditorBase::DataEditorBase()
				{
				}

				DataEditorBase::~DataEditorBase()
				{
					if (editorTemplate)
					{
						SafeDeleteComposition(editorTemplate);
					}
				}

				IDataEditorFactory* DataEditorBase::GetFactory()
				{
					return factory;
				}

				templates::GuiGridEditorTemplate* DataEditorBase::GetTemplate()
				{
					return editorTemplate;
				}

				void DataEditorBase::NotifyDeletedTemplate()
				{
					editorTemplate = nullptr;
				}

				void DataEditorBase::BeforeEditCell(GuiListControl::IItemProvider* itemProvider, vint row, vint column)
				{
					if (auto listViewItemView = dynamic_cast<IListViewItemView*>(dataGridContext->GetItemProvider()->RequestView(IListViewItemView::Identifier)))
					{
						auto style = dataGridContext->GetListViewControlTemplate();
						editorTemplate->SetPrimaryTextColor(style->GetPrimaryTextColor());
						editorTemplate->SetSecondaryTextColor(style->GetSecondaryTextColor());
						editorTemplate->SetItemSeparatorColor(style->GetItemSeparatorColor());

						editorTemplate->SetLargeImage(listViewItemView->GetLargeImage(row));
						editorTemplate->SetSmallImage(listViewItemView->GetSmallImage(row));
						editorTemplate->SetText(column == 0 ? listViewItemView->GetText(row) : listViewItemView->GetSubItem(row, column - 1));
					}
					if (auto dataGridView = dynamic_cast<IDataGridView*>(dataGridContext->GetItemProvider()->RequestView(IDataGridView::Identifier)))
					{
						editorTemplate->SetRowValue(itemProvider->GetBindingValue(row));
						editorTemplate->SetCellValue(dataGridView->GetBindingCellValue(row, column));
					}
					editorTemplate->CellValueChanged.AttachMethod(this, &DataEditorBase::OnCellValueChanged);
				}

				bool DataEditorBase::GetCellValueSaved()
				{
					if (editorTemplate)
					{
						return editorTemplate->GetCellValueSaved();
					}
					return true;
				}

/***********************************************************************
DataEditorFactory
***********************************************************************/

				DataEditorFactory::DataEditorFactory(TemplateProperty<GuiGridEditorTemplate> _templateFactory)
					:templateFactory(_templateFactory)
				{
				}

				DataEditorFactory::~DataEditorFactory()
				{
				}

				Ptr<IDataEditor> DataEditorFactory::CreateEditor(controls::list::IDataGridContext* dataGridContext)
				{
					auto editor = MakePtr<DataEditorBase>();
					editor->factory = this;
					editor->dataGridContext = dataGridContext;

					ItemTemplate* itemTemplate = templateFactory({});
					CHECK_ERROR(itemTemplate, L"DataEditorFactory::CreateEditor(IDataGridContext*)#An instance of GuiGridEditorTemplate is expected.");
					editor->editorTemplate = itemTemplate;
					return editor;
				}
				
/***********************************************************************
MainColumnVisualizerTemplate
***********************************************************************/

				void MainColumnVisualizerTemplate::OnTextChanged(GuiGraphicsComposition* sender, compositions::GuiEventArgs& arguments)
				{
					text->SetText(GetText());
				}

				void MainColumnVisualizerTemplate::OnFontChanged(GuiGraphicsComposition* sender, compositions::GuiEventArgs& arguments)
				{
					text->SetFont(GetFont());
				}

				void MainColumnVisualizerTemplate::OnTextColorChanged(GuiGraphicsComposition* sender, compositions::GuiEventArgs& arguments)
				{
					text->SetColor(GetPrimaryTextColor());
				}

				void MainColumnVisualizerTemplate::OnSmallImageChanged(GuiGraphicsComposition* sender, compositions::GuiEventArgs& arguments)
				{
					auto imageData = GetSmallImage();
					if (imageData)
					{
						image->SetImage(imageData->GetImage(), imageData->GetFrameIndex());
					}
					else
					{
						image->SetImage(nullptr);
					}
				}

				MainColumnVisualizerTemplate::MainColumnVisualizerTemplate()
				{
					GuiTableComposition* table = new GuiTableComposition;
					table->SetMinSizeLimitation(GuiGraphicsComposition::LimitToElementAndChildren);
					table->SetRowsAndColumns(3, 2);
					table->SetRowOption(0, GuiCellOption::PercentageOption(0.5));
					table->SetRowOption(1, GuiCellOption::MinSizeOption());
					table->SetRowOption(2, GuiCellOption::PercentageOption(0.5));
					table->SetColumnOption(0, GuiCellOption::MinSizeOption());
					table->SetColumnOption(1, GuiCellOption::PercentageOption(1.0));
					table->SetCellPadding(2);
					{
						GuiCellComposition* cell = new GuiCellComposition;
						table->AddChild(cell);
						cell->SetSite(1, 0, 1, 1);
						cell->SetPreferredMinSize(Size(16, 16));

						image = GuiImageFrameElement::Create();
						image->SetStretch(true);
						cell->SetOwnedElement(image);
					}
					{
						GuiCellComposition* cell = new GuiCellComposition;
						table->AddChild(cell);
						cell->SetSite(0, 1, 3, 1);
						cell->SetMargin(Margin(0, 0, 8, 0));

						text = GuiSolidLabelElement::Create();
						text->SetAlignments(Alignment::Left, Alignment::Center);
						text->SetEllipse(true);
						cell->SetOwnedElement(text);
					}
					table->SetAlignmentToParent(Margin(0, 0, 0, 0));

					SetMinSizeLimitation(GuiGraphicsComposition::LimitToElementAndChildren);
					AddChild(table);

					TextChanged.AttachMethod(this, &MainColumnVisualizerTemplate::OnTextChanged);
					FontChanged.AttachMethod(this, &MainColumnVisualizerTemplate::OnFontChanged);
					PrimaryTextColorChanged.AttachMethod(this, &MainColumnVisualizerTemplate::OnTextColorChanged);
					SmallImageChanged.AttachMethod(this, &MainColumnVisualizerTemplate::OnSmallImageChanged);

					TextChanged.Execute(compositions::GuiEventArgs(this));
					FontChanged.Execute(compositions::GuiEventArgs(this));
					PrimaryTextColorChanged.Execute(compositions::GuiEventArgs(this));
					SmallImageChanged.Execute(compositions::GuiEventArgs(this));
				}

				MainColumnVisualizerTemplate::~MainColumnVisualizerTemplate()
				{
				}
				
/***********************************************************************
SubColumnVisualizerTemplate
***********************************************************************/

				void SubColumnVisualizerTemplate::OnTextChanged(GuiGraphicsComposition* sender, compositions::GuiEventArgs& arguments)
				{
					text->SetText(GetText());
				}

				void SubColumnVisualizerTemplate::OnFontChanged(GuiGraphicsComposition* sender, compositions::GuiEventArgs& arguments)
				{
					text->SetFont(GetFont());
				}

				void SubColumnVisualizerTemplate::OnTextColorChanged(GuiGraphicsComposition* sender, compositions::GuiEventArgs& arguments)
				{
					text->SetColor(GetSecondaryTextColor());
				}

				void SubColumnVisualizerTemplate::Initialize(bool fixTextColor)
				{
					text = GuiSolidLabelElement::Create();
					text->SetVerticalAlignment(Alignment::Center);

					SetMinSizeLimitation(GuiGraphicsComposition::LimitToElementAndChildren);
					SetMargin(Margin(8, 0, 8, 0));
					SetOwnedElement(text);

					TextChanged.AttachMethod(this, &SubColumnVisualizerTemplate::OnTextChanged);
					FontChanged.AttachMethod(this, &SubColumnVisualizerTemplate::OnFontChanged);
					if (!fixTextColor)
					{
						SecondaryTextColorChanged.AttachMethod(this, &SubColumnVisualizerTemplate::OnTextColorChanged);
					}

					TextChanged.Execute(compositions::GuiEventArgs(this));
					FontChanged.Execute(compositions::GuiEventArgs(this));
					if (!fixTextColor)
					{
						SecondaryTextColorChanged.Execute(compositions::GuiEventArgs(this));
					}
				}

				SubColumnVisualizerTemplate::SubColumnVisualizerTemplate(bool fixTextColor)
				{
					Initialize(fixTextColor);
				}

				SubColumnVisualizerTemplate::SubColumnVisualizerTemplate()
				{
					Initialize(false);
				}

				SubColumnVisualizerTemplate::~SubColumnVisualizerTemplate()
				{
				}
				
/***********************************************************************
HyperlinkVisualizerTemplate
***********************************************************************/

				void HyperlinkVisualizerTemplate::label_MouseEnter(compositions::GuiGraphicsComposition* sender, compositions::GuiEventArgs& arguments)
				{
					FontProperties font = text->GetFont();
					font.underline = true;
					text->SetFont(font);
				}

				void HyperlinkVisualizerTemplate::label_MouseLeave(compositions::GuiGraphicsComposition* sender, compositions::GuiEventArgs& arguments)
				{
					FontProperties font = text->GetFont();
					font.underline = false;
					text->SetFont(font);
				}

				HyperlinkVisualizerTemplate::HyperlinkVisualizerTemplate()
					:SubColumnVisualizerTemplate(true)
				{
					text->SetColor(Color(0, 0, 255));
					text->SetEllipse(true);
					GetEventReceiver()->mouseEnter.AttachMethod(this, &HyperlinkVisualizerTemplate::label_MouseEnter);
					GetEventReceiver()->mouseLeave.AttachMethod(this, &HyperlinkVisualizerTemplate::label_MouseLeave);
					SetAssociatedCursor(GetCurrentController()->ResourceService()->GetSystemCursor(INativeCursor::Hand));
				}

				HyperlinkVisualizerTemplate::~HyperlinkVisualizerTemplate()
				{
				}
				
/***********************************************************************
CellBorderVisualizerTemplate
***********************************************************************/

				void CellBorderVisualizerTemplate::OnItemSeparatorColorChanged(GuiGraphicsComposition* sender, compositions::GuiEventArgs& arguments)
				{
					border1->SetColor(GetItemSeparatorColor());
					border2->SetColor(GetItemSeparatorColor());
				}

				CellBorderVisualizerTemplate::CellBorderVisualizerTemplate()
				{
					GuiBoundsComposition* bounds1 = nullptr;
					GuiBoundsComposition* bounds2 = nullptr;
					{
						border1 = GuiSolidBorderElement::Create();

						bounds1 = new GuiBoundsComposition;
						bounds1->SetOwnedElement(border1);
						bounds1->SetAlignmentToParent(Margin(-1, 0, 0, 0));
					}
					{
						border2 = GuiSolidBorderElement::Create();

						bounds2 = new GuiBoundsComposition;
						bounds2->SetOwnedElement(border2);
						bounds2->SetAlignmentToParent(Margin(0, -1, 0, 0));
					}

					SetAlignmentToParent(Margin(0, 0, 1, 1));
					SetMinSizeLimitation(GuiGraphicsComposition::LimitToElementAndChildren);
					AddChild(bounds1);
					AddChild(bounds2);

					ItemSeparatorColorChanged.AttachMethod(this, &CellBorderVisualizerTemplate::OnItemSeparatorColorChanged);

					ItemSeparatorColorChanged.Execute(compositions::GuiEventArgs(this));
				}

				CellBorderVisualizerTemplate::~CellBorderVisualizerTemplate()
				{
				}
			}
		}
	}
}

/***********************************************************************
.\CONTROLS\TEXTEDITORPACKAGE\EDITORCALLBACK\GUITEXTAUTOCOMPLETE.CPP
***********************************************************************/

namespace vl
{
	namespace presentation
	{
		namespace controls
		{
			using namespace collections;

/***********************************************************************
GuiTextBoxAutoCompleteBase::TextListControlProvider
***********************************************************************/

			GuiTextBoxAutoCompleteBase::TextListControlProvider::TextListControlProvider(TemplateProperty<templates::GuiTextListTemplate> controlTemplate)
			{
				autoCompleteList = new GuiTextList(theme::ThemeName::TextList);
				if (controlTemplate)
				{
					autoCompleteList->SetControlTemplate(controlTemplate);
				}
				autoCompleteList->SetHorizontalAlwaysVisible(false);
				autoCompleteList->SetVerticalAlwaysVisible(false);
			}

			GuiTextBoxAutoCompleteBase::TextListControlProvider::~TextListControlProvider()
			{
			}

			GuiControl* GuiTextBoxAutoCompleteBase::TextListControlProvider::GetAutoCompleteControl()
			{
				return autoCompleteList;
			}

			GuiSelectableListControl* GuiTextBoxAutoCompleteBase::TextListControlProvider::GetListControl()
			{
				return autoCompleteList;
			}

			void GuiTextBoxAutoCompleteBase::TextListControlProvider::SetSortedContent(const collections::List<AutoCompleteItem>& items)
			{
				autoCompleteList->GetItems().Clear();
				FOREACH(AutoCompleteItem, item, items)
				{
					autoCompleteList->GetItems().Add(new list::TextItem(item.text));
				}
			}

			vint GuiTextBoxAutoCompleteBase::TextListControlProvider::GetItemCount()
			{
				return autoCompleteList->GetItems().Count();
			}

			WString GuiTextBoxAutoCompleteBase::TextListControlProvider::GetItemText(vint index)
			{
				return autoCompleteList->GetItems()[index]->GetText();
			}

/***********************************************************************
GuiTextBoxAutoCompleteBase
***********************************************************************/

			bool GuiTextBoxAutoCompleteBase::IsPrefix(const WString& prefix, const WString& candidate)
			{
				if(candidate.Length()>=prefix.Length())
				{
					if(INVLOC.Compare(prefix, candidate.Sub(0, prefix.Length()), Locale::IgnoreCase)==0)
					{
						return true;
					}
				}
				return false;
			}

			GuiTextBoxAutoCompleteBase::GuiTextBoxAutoCompleteBase(Ptr<IAutoCompleteControlProvider> _autoCompleteControlProvider)
				:element(0)
				, elementModifyLock(0)
				, ownerComposition(0)
				, autoCompleteControlProvider(_autoCompleteControlProvider)
			{
				if (!autoCompleteControlProvider)
				{
					autoCompleteControlProvider = new TextListControlProvider;
				}
				autoCompleteControlProvider->GetAutoCompleteControl()->GetBoundsComposition()->SetAlignmentToParent(Margin(0, 0, 0, 0));

				autoCompletePopup = new GuiPopup(theme::ThemeName::Menu);
				autoCompletePopup->AddChild(autoCompleteControlProvider->GetAutoCompleteControl());
			}

			GuiTextBoxAutoCompleteBase::~GuiTextBoxAutoCompleteBase()
			{
				delete autoCompletePopup;
			}

			void GuiTextBoxAutoCompleteBase::Attach(elements::GuiColorizedTextElement* _element, SpinLock& _elementModifyLock, compositions::GuiGraphicsComposition* _ownerComposition, vuint editVersion)
			{
				if(_element)
				{
					SPIN_LOCK(_elementModifyLock)
					{
						element=_element;
						elementModifyLock=&_elementModifyLock;
						ownerComposition=_ownerComposition;
					}
				}
			}

			void GuiTextBoxAutoCompleteBase::Detach()
			{
				if(element && elementModifyLock)
				{
					SPIN_LOCK(*elementModifyLock)
					{
						element=0;
						elementModifyLock=0;
					}
				}
			}

			void GuiTextBoxAutoCompleteBase::TextEditPreview(TextEditPreviewStruct& arguments)
			{
			}

			void GuiTextBoxAutoCompleteBase::TextEditNotify(const TextEditNotifyStruct& arguments)
			{
				if(element && elementModifyLock)
				{
					if(IsListOpening())
					{
						TextPos begin=GetListStartPosition();
						TextPos end=arguments.inputEnd;
						WString editingText=element->GetLines().GetText(begin, end);
						HighlightList(editingText);
					}
				}
			}

			void GuiTextBoxAutoCompleteBase::TextCaretChanged(const TextCaretChangedStruct& arguments)
			{
			}

			void GuiTextBoxAutoCompleteBase::TextEditFinished(vuint editVersion)
			{
			}

			bool GuiTextBoxAutoCompleteBase::IsListOpening()
			{
				return autoCompletePopup->GetOpening();
			}

			void GuiTextBoxAutoCompleteBase::OpenList(TextPos startPosition)
			{
				if(element && elementModifyLock)
				{
					autoCompleteStartPosition=startPosition;
					Rect bounds=element->GetLines().GetRectFromTextPos(startPosition);
					Point viewPosition=element->GetViewPosition();
					GuiControl* ownerControl=ownerComposition->GetRelatedControl();
					Rect compositionBounds=ownerComposition->GetGlobalBounds();
					Rect controlBounds=ownerControl->GetBoundsComposition()->GetGlobalBounds();
					vint px=compositionBounds.x1-controlBounds.x1-viewPosition.x;
					vint py=compositionBounds.y1-controlBounds.y1-viewPosition.y;
					bounds.x1+=px;
					bounds.x2+=px;
					bounds.y1+=py+5;
					bounds.y2+=py+5;
					autoCompletePopup->ShowPopup(ownerControl, bounds, true);
				}
			}

			void GuiTextBoxAutoCompleteBase::CloseList()
			{
				autoCompletePopup->Close();
			}

			void GuiTextBoxAutoCompleteBase::SetListContent(const collections::List<AutoCompleteItem>& items)
			{
				if(items.Count()==0)
				{
					CloseList();
				}

				List<AutoCompleteItem> sortedItems;
				CopyFrom(
					sortedItems,
					From(items)
						.OrderBy([](const AutoCompleteItem& a, const AutoCompleteItem& b)
						{
							return INVLOC.Compare(a.text, b.text, Locale::IgnoreCase);
						})
					);

				autoCompleteControlProvider->SetSortedContent(sortedItems);
				autoCompleteControlProvider->GetAutoCompleteControl()->GetBoundsComposition()->SetPreferredMinSize(Size(200, 200));
			}

			TextPos GuiTextBoxAutoCompleteBase::GetListStartPosition()
			{
				return autoCompleteStartPosition;
			}

			bool GuiTextBoxAutoCompleteBase::SelectPreviousListItem()
			{
				if(!IsListOpening()) return false;
				if(autoCompleteControlProvider->GetListControl()->GetSelectedItems().Count()==0)
				{
					autoCompleteControlProvider->GetListControl()->SetSelected(0, true);
				}
				else
				{
					vint index=autoCompleteControlProvider->GetListControl()->GetSelectedItems()[0];
					if (index > 0) index--;
					autoCompleteControlProvider->GetListControl()->SetSelected(index, true);
					autoCompleteControlProvider->GetListControl()->EnsureItemVisible(index);
				}
				return true;
			}

			bool GuiTextBoxAutoCompleteBase::SelectNextListItem()
			{
				if(!IsListOpening()) return false;
				if (autoCompleteControlProvider->GetListControl()->GetSelectedItems().Count() == 0)
				{
					autoCompleteControlProvider->GetListControl()->SetSelected(0, true);
				}
				else
				{
					vint index = autoCompleteControlProvider->GetListControl()->GetSelectedItems()[0];
					if (index < autoCompleteControlProvider->GetItemCount() - 1) index++;
					autoCompleteControlProvider->GetListControl()->SetSelected(index, true);
					autoCompleteControlProvider->GetListControl()->EnsureItemVisible(index);
				}
				return true;
			}

			bool GuiTextBoxAutoCompleteBase::ApplySelectedListItem()
			{
				if(!IsListOpening()) return false;
				if(!ownerComposition) return false;
				const auto& selectedItems = autoCompleteControlProvider->GetListControl()->GetSelectedItems();
				if (selectedItems.Count() == 0) return false;
				GuiTextBoxCommonInterface* ci=dynamic_cast<GuiTextBoxCommonInterface*>(ownerComposition->GetRelatedControl());
				if(!ci) return false;

				vint index = selectedItems[0];
				WString selectedItem = autoCompleteControlProvider->GetItemText(index);
				TextPos begin = autoCompleteStartPosition;
				TextPos end = ci->GetCaretEnd();
				ci->Select(begin, end);
				ci->SetSelectionText(selectedItem);
				CloseList();
				return true;
			}

			WString GuiTextBoxAutoCompleteBase::GetSelectedListItem()
			{
				if(!IsListOpening()) return L"";
				const auto& selectedItems = autoCompleteControlProvider->GetListControl()->GetSelectedItems();
				if (selectedItems.Count() == 0) return L"";
				vint index = selectedItems[0];
				return autoCompleteControlProvider->GetItemText(index);
			}

			void GuiTextBoxAutoCompleteBase::HighlightList(const WString& editingText)
			{
				if(IsListOpening())
				{
					vint first=0;
					vint last = autoCompleteControlProvider->GetItemCount() - 1;
					vint selected=-1;

					while (first <= last)
					{
						vint middle = (first + last) / 2;
						WString text = autoCompleteControlProvider->GetItemText(middle);
						if (IsPrefix(editingText, text))
						{
							selected = middle;
							break;
						}

						vint result = INVLOC.Compare(editingText, text, Locale::IgnoreCase);
						if (result <= 0)
						{
							last = middle - 1;
						}
						else
						{
							first = middle + 1;
						}
					}

					while(selected>0)
					{
						WString text = autoCompleteControlProvider->GetItemText(selected - 1);
						if (IsPrefix(editingText, text))
						{
							selected--;
						}
						else
						{
							break;
						}
					}

					if(selected!=-1)
					{
						autoCompleteControlProvider->GetListControl()->SetSelected(selected, true);
						autoCompleteControlProvider->GetListControl()->EnsureItemVisible(selected);
					}
				}
			}
		}
	}
}

/***********************************************************************
.\CONTROLS\TEXTEDITORPACKAGE\GUITEXTCOMMONINTERFACE.CPP
***********************************************************************/

namespace vl
{
	namespace presentation
	{
		namespace controls
		{
			using namespace elements;
			using namespace elements::text;
			using namespace compositions;

/***********************************************************************
GuiTextBoxCommonInterface::DefaultCallback
***********************************************************************/

			GuiTextBoxCommonInterface::DefaultCallback::DefaultCallback(elements::GuiColorizedTextElement* _textElement, compositions::GuiGraphicsComposition* _textComposition)
				:textElement(_textElement)
				,textComposition(_textComposition)
			{
			}

			GuiTextBoxCommonInterface::DefaultCallback::~DefaultCallback()
			{
			}

			TextPos GuiTextBoxCommonInterface::DefaultCallback::GetLeftWord(TextPos pos)
			{
				return pos;
			}

			TextPos GuiTextBoxCommonInterface::DefaultCallback::GetRightWord(TextPos pos)
			{
				return pos;
			}

			void GuiTextBoxCommonInterface::DefaultCallback::GetWord(TextPos pos, TextPos& begin, TextPos& end)
			{
				begin=pos;
				end=pos;
			}

			vint GuiTextBoxCommonInterface::DefaultCallback::GetPageRows()
			{
				return textComposition->GetBounds().Height()/textElement->GetLines().GetRowHeight();
			}

			bool GuiTextBoxCommonInterface::DefaultCallback::BeforeModify(TextPos start, TextPos end, const WString& originalText, WString& inputText)
			{
				return true;
			}

/***********************************************************************
GuiTextBoxCommonInterface
***********************************************************************/
			
			void GuiTextBoxCommonInterface::InvokeUndoRedoChanged()
			{
				UndoRedoChanged.Execute(textControl->GetNotifyEventArguments());
			}

			void GuiTextBoxCommonInterface::InvokeModifiedChanged()
			{
				ModifiedChanged.Execute(textControl->GetNotifyEventArguments());
			}

			void GuiTextBoxCommonInterface::UpdateCaretPoint()
			{
				GuiGraphicsHost* host=textComposition->GetRelatedGraphicsHost();
				if(host)
				{
					Rect caret=textElement->GetLines().GetRectFromTextPos(textElement->GetCaretEnd());
					Point view=textElement->GetViewPosition();
					vint x=caret.x1-view.x;
					vint y=caret.y2-view.y;
					host->SetCaretPoint(Point(x, y), textComposition);
				}
			}

			void GuiTextBoxCommonInterface::Move(TextPos pos, bool shift)
			{
				TextPos oldBegin=textElement->GetCaretBegin();
				TextPos oldEnd=textElement->GetCaretEnd();

				pos=textElement->GetLines().Normalize(pos);
				if(!shift)
				{
					textElement->SetCaretBegin(pos);
				}
				textElement->SetCaretEnd(pos);
				if(textControl)
				{
					GuiGraphicsHost* host=textComposition->GetRelatedGraphicsHost();
					if(host)
					{
						if(host->GetFocusedComposition()==textControl->GetFocusableComposition())
						{
							textElement->SetCaretVisible(true);
						}
					}
				}

				Rect bounds=textElement->GetLines().GetRectFromTextPos(pos);
				Rect view=Rect(textElement->GetViewPosition(), textComposition->GetBounds().GetSize());
				Point viewPoint=view.LeftTop();

				if(view.x2>view.x1 && view.y2>view.y1)
				{
					if(bounds.x1<view.x1)
					{
						viewPoint.x=bounds.x1;
					}
					else if(bounds.x2>view.x2)
					{
						viewPoint.x=bounds.x2-view.Width();
					}
					if(bounds.y1<view.y1)
					{
						viewPoint.y=bounds.y1;
					}
					else if(bounds.y2>view.y2)
					{
						viewPoint.y=bounds.y2-view.Height();
					}
				}

				callback->ScrollToView(viewPoint);
				UpdateCaretPoint();

				TextPos newBegin=textElement->GetCaretBegin();
				TextPos newEnd=textElement->GetCaretEnd();
				if(oldBegin!=newBegin || oldEnd!=newEnd)
				{
					ICommonTextEditCallback::TextCaretChangedStruct arguments;
					arguments.oldBegin=oldBegin;
					arguments.oldEnd=oldEnd;
					arguments.newBegin=newBegin;
					arguments.newEnd=newEnd;
					arguments.editVersion=editVersion;
					for(vint i=0;i<textEditCallbacks.Count();i++)
					{
						textEditCallbacks[i]->TextCaretChanged(arguments);
					}
					SelectionChanged.Execute(textControl->GetNotifyEventArguments());
				}
			}

			void GuiTextBoxCommonInterface::Modify(TextPos start, TextPos end, const WString& input, bool asKeyInput)
			{
				if(start>end)
				{
					TextPos temp=start;
					start=end;
					end=temp;
				}
				TextPos originalStart=start;
				TextPos originalEnd=end;
				WString originalText=textElement->GetLines().GetText(start, end);
				WString inputText=input;
				if(callback->BeforeModify(start, end, originalText, inputText))
				{
					{
						ICommonTextEditCallback::TextEditPreviewStruct arguments;
						arguments.originalStart=originalStart;
						arguments.originalEnd=originalEnd;
						arguments.originalText=originalText;
						arguments.inputText=inputText;
						arguments.editVersion=editVersion;
						arguments.keyInput=asKeyInput;
						for(vint i=0;i<textEditCallbacks.Count();i++)
						{
							textEditCallbacks[i]->TextEditPreview(arguments);
						}
						
						inputText=arguments.inputText;
						if(originalStart!=arguments.originalStart || originalEnd!=arguments.originalEnd)
						{
							originalStart=arguments.originalStart;
							originalEnd=arguments.originalEnd;
							originalText=textElement->GetLines().GetText(originalStart, originalEnd);
							start=originalStart;
							end=originalEnd;
						}
					}

					SPIN_LOCK(elementModifyLock)
					{
						end=textElement->GetLines().Modify(start, end, inputText);
					}
					callback->AfterModify(originalStart, originalEnd, originalText, start, end, inputText);
					
					editVersion++;
					{
						ICommonTextEditCallback::TextEditNotifyStruct arguments;
						arguments.originalStart=originalStart;
						arguments.originalEnd=originalEnd;
						arguments.originalText=originalText;
						arguments.inputStart=start;
						arguments.inputEnd=end;
						arguments.inputText=inputText;
						arguments.editVersion=editVersion;
						arguments.keyInput=asKeyInput;
						for(vint i=0;i<textEditCallbacks.Count();i++)
						{
							textEditCallbacks[i]->TextEditNotify(arguments);
						}
					}

					Move(end, false);
					
					for(vint i=0;i<textEditCallbacks.Count();i++)
					{
						textEditCallbacks[i]->TextEditFinished(editVersion);
					}

					textControl->TextChanged.Execute(textControl->GetNotifyEventArguments());
				}
			}

			bool GuiTextBoxCommonInterface::ProcessKey(vint code, bool shift, bool ctrl)
			{
				if(IGuiShortcutKeyItem* item=internalShortcutKeyManager->TryGetShortcut(ctrl, shift, false, code))
				{
					GuiEventArgs arguments;
					item->Executed.Execute(arguments);
					return true;
				}

				TextPos begin=textElement->GetCaretBegin();
				TextPos end=textElement->GetCaretEnd();
				switch(code)
				{
				case VKEY_ESCAPE:
					if(autoComplete && autoComplete->IsListOpening() && !shift && !ctrl)
					{
						autoComplete->CloseList();
					}
					return true;
				case VKEY_RETURN:
					if(autoComplete && autoComplete->IsListOpening() && !shift && !ctrl)
					{
						if(autoComplete->ApplySelectedListItem())
						{
							preventEnterDueToAutoComplete=true;
							return true;
						}
					}
					break;
				case VKEY_UP:
					if(autoComplete && autoComplete->IsListOpening() && !shift && !ctrl)
					{
						autoComplete->SelectPreviousListItem();
					}
					else
					{
						end.row--;
						Move(end, shift);
					}
					return true;
				case VKEY_DOWN:
					if(autoComplete && autoComplete->IsListOpening() && !shift && !ctrl)
					{
						autoComplete->SelectNextListItem();
					}
					else
					{
						end.row++;
						Move(end, shift);
					}
					return true;
				case VKEY_LEFT:
					{
						if(ctrl)
						{
							Move(callback->GetLeftWord(end), shift);
						}
						else
						{
							if(end.column==0)
							{
								if(end.row>0)
								{
									end.row--;
									end=textElement->GetLines().Normalize(end);
									end.column=textElement->GetLines().GetLine(end.row).dataLength;
								}
							}
							else
							{
								end.column--;
							}
							Move(end, shift);
						}
					}
					return true;
				case VKEY_RIGHT:
					{
						if(ctrl)
						{
							Move(callback->GetRightWord(end), shift);
						}
						else
						{
							if(end.column==textElement->GetLines().GetLine(end.row).dataLength)
							{
								if(end.row<textElement->GetLines().GetCount()-1)
								{
									end.row++;
									end.column=0;
								}
							}
							else
							{
								end.column++;
							}
							Move(end, shift);
						}
					}
					return true;
				case VKEY_HOME:
					{
						if(ctrl)
						{
							Move(TextPos(0, 0), shift);
						}
						else
						{
							end.column=0;
							Move(end, shift);
						}
					}
					return true;
				case VKEY_END:
					{
						if(ctrl)
						{
							end.row=textElement->GetLines().GetCount()-1;
						}
						end.column=textElement->GetLines().GetLine(end.row).dataLength;
						Move(end, shift);
					}
					return true;
				case VKEY_PRIOR:
					{
						end.row-=callback->GetPageRows();
						Move(end, shift);
					}
					return true;
				case VKEY_NEXT:
					{
						end.row+=callback->GetPageRows();
						Move(end, shift);
					}
					return true;
				case VKEY_BACK:
					if(!readonly)
					{
						if(ctrl && !shift)
						{
							ProcessKey(VKEY_LEFT, true, true);
							ProcessKey(VKEY_BACK, false, false);
						}
						else if(!ctrl && shift)
						{
							ProcessKey(VKEY_UP, true, false);
							ProcessKey(VKEY_BACK, false, false);
						}
						else
						{
							if(begin==end)
							{
								ProcessKey(VKEY_LEFT, true, false);
							}
							SetSelectionTextAsKeyInput(L"");
						}
						return true;
					}
					break;
				case VKEY_DELETE:
					if(!readonly)
					{
						if(ctrl && !shift)
						{
							ProcessKey(VKEY_RIGHT, true, true);
							ProcessKey(VKEY_DELETE, false, false);
						}
						else if(!ctrl && shift)
						{
							ProcessKey(VKEY_DOWN, true, false);
							ProcessKey(VKEY_DELETE, false, false);
						}
						else
						{
							if(begin==end)
							{
								ProcessKey(VKEY_RIGHT, true, false);
							}
							SetSelectionTextAsKeyInput(L"");
						}
						return true;
					}
					break;
				}
				return false;
			}

			void GuiTextBoxCommonInterface::OnGotFocus(compositions::GuiGraphicsComposition* sender, compositions::GuiEventArgs& arguments)
			{
				textElement->SetFocused(true);
				textElement->SetCaretVisible(true);
				UpdateCaretPoint();
			}

			void GuiTextBoxCommonInterface::OnLostFocus(compositions::GuiGraphicsComposition* sender, compositions::GuiEventArgs& arguments)
			{
				textElement->SetFocused(false);
				textElement->SetCaretVisible(false);
			}

			void GuiTextBoxCommonInterface::OnCaretNotify(compositions::GuiGraphicsComposition* sender, compositions::GuiEventArgs& arguments)
			{
				textElement->SetCaretVisible(!textElement->GetCaretVisible());
			}

			void GuiTextBoxCommonInterface::OnLeftButtonDown(compositions::GuiGraphicsComposition* sender, compositions::GuiMouseEventArgs& arguments)
			{
				if(textControl->GetVisuallyEnabled() && arguments.compositionSource==arguments.eventSource)
				{
					dragging=true;
					TextPos pos=GetNearestTextPos(Point(arguments.x, arguments.y));
					Move(pos, arguments.shift);
				}
			}

			void GuiTextBoxCommonInterface::OnLeftButtonUp(compositions::GuiGraphicsComposition* sender, compositions::GuiMouseEventArgs& arguments)
			{
				if(textControl->GetVisuallyEnabled() && arguments.compositionSource==arguments.eventSource)
				{
					dragging=false;
				}
			}

			void GuiTextBoxCommonInterface::OnMouseMove(compositions::GuiGraphicsComposition* sender, compositions::GuiMouseEventArgs& arguments)
			{
				if(textControl->GetVisuallyEnabled() && arguments.compositionSource==arguments.eventSource)
				{
					if(dragging)
					{
						TextPos pos=GetNearestTextPos(Point(arguments.x, arguments.y));
						Move(pos, true);
					}
				}
			}

			void GuiTextBoxCommonInterface::OnKeyDown(compositions::GuiGraphicsComposition* sender, compositions::GuiKeyEventArgs& arguments)
			{
				if(textControl->GetVisuallyEnabled() && arguments.compositionSource==arguments.eventSource)
				{
					if(ProcessKey(arguments.code, arguments.shift, arguments.ctrl))
					{
						arguments.handled=true;
					}
				}
			}

			void GuiTextBoxCommonInterface::OnCharInput(compositions::GuiGraphicsComposition* sender, compositions::GuiCharEventArgs& arguments)
			{
				if(preventEnterDueToAutoComplete)
				{
					preventEnterDueToAutoComplete=false;
					if(arguments.code==VKEY_RETURN)
					{
						return;
					}
				}
				if(textControl->GetVisuallyEnabled() && arguments.compositionSource==arguments.eventSource)
				{
					if(!readonly && arguments.code!=VKEY_ESCAPE && arguments.code!=VKEY_BACK && !arguments.ctrl)
					{
						SetSelectionTextAsKeyInput(WString(arguments.code));
					}
				}
			}

			void GuiTextBoxCommonInterface::Install(
				elements::GuiColorizedTextElement* _textElement,
				compositions::GuiGraphicsComposition* _textComposition,
				GuiControl* _textControl,
				compositions::GuiGraphicsComposition* eventComposition,
				compositions::GuiGraphicsComposition* focusableComposition
			)
			{
				textElement=_textElement;
				textComposition=_textComposition;
				textControl=_textControl;
				textComposition->SetAssociatedCursor(GetCurrentController()->ResourceService()->GetSystemCursor(INativeCursor::IBeam));
				SelectionChanged.SetAssociatedComposition(eventComposition);
				UndoRedoChanged.SetAssociatedComposition(eventComposition);
				ModifiedChanged.SetAssociatedComposition(eventComposition);

				undoRedoProcessor->UndoRedoChanged.Add(this, &GuiTextBoxCommonInterface::InvokeUndoRedoChanged);
				undoRedoProcessor->ModifiedChanged.Add(this, &GuiTextBoxCommonInterface::InvokeModifiedChanged);

				focusableComposition->GetEventReceiver()->gotFocus.AttachMethod(this, &GuiTextBoxCommonInterface::OnGotFocus);
				focusableComposition->GetEventReceiver()->lostFocus.AttachMethod(this, &GuiTextBoxCommonInterface::OnLostFocus);
				focusableComposition->GetEventReceiver()->caretNotify.AttachMethod(this, &GuiTextBoxCommonInterface::OnCaretNotify);
				textComposition->GetEventReceiver()->leftButtonDown.AttachMethod(this, &GuiTextBoxCommonInterface::OnLeftButtonDown);
				textComposition->GetEventReceiver()->leftButtonUp.AttachMethod(this, &GuiTextBoxCommonInterface::OnLeftButtonUp);
				textComposition->GetEventReceiver()->mouseMove.AttachMethod(this, &GuiTextBoxCommonInterface::OnMouseMove);
				focusableComposition->GetEventReceiver()->keyDown.AttachMethod(this, &GuiTextBoxCommonInterface::OnKeyDown);
				focusableComposition->GetEventReceiver()->charInput.AttachMethod(this, &GuiTextBoxCommonInterface::OnCharInput);

				for(vint i=0;i<textEditCallbacks.Count();i++)
				{
					textEditCallbacks[i]->Attach(textElement, elementModifyLock, textComposition ,editVersion);
				}
			}
			
			GuiTextBoxCommonInterface::ICallback* GuiTextBoxCommonInterface::GetCallback()
			{
				return callback;
			}

			void GuiTextBoxCommonInterface::SetCallback(ICallback* value)
			{
				callback=value;
			}

			bool GuiTextBoxCommonInterface::AttachTextEditCallback(Ptr<ICommonTextEditCallback> value)
			{
				if(textEditCallbacks.Contains(value.Obj()))
				{
					return false;
				}
				else
				{
					textEditCallbacks.Add(value);
					if(textElement)
					{
						value->Attach(textElement, elementModifyLock, textComposition, editVersion);
					}
					return true;
				}
			}

			bool GuiTextBoxCommonInterface::DetachTextEditCallback(Ptr<ICommonTextEditCallback> value)
			{
				if(textEditCallbacks.Remove(value.Obj()))
				{
					value->Detach();
					return true;
				}
				else
				{
					return false;
				}
			}

			void GuiTextBoxCommonInterface::AddShortcutCommand(vint key, const Func<void()>& eventHandler)
			{
				IGuiShortcutKeyItem* item=internalShortcutKeyManager->CreateShortcut(true, false, false, key);
				item->Executed.AttachLambda([=](GuiGraphicsComposition* sender, GuiEventArgs& arguments)
				{
					eventHandler();
				});
			}

			elements::GuiColorizedTextElement* GuiTextBoxCommonInterface::GetTextElement()
			{
				return textElement;
			}

			void GuiTextBoxCommonInterface::UnsafeSetText(const WString& value)
			{
				if(textElement)
				{
					TextPos end;
					if(textElement->GetLines().GetCount()>0)
					{
						end.row=textElement->GetLines().GetCount()-1;
						end.column=textElement->GetLines().GetLine(end.row).dataLength;
					}
					Modify(TextPos(), end, value, false);
				}
			}

			GuiTextBoxCommonInterface::GuiTextBoxCommonInterface()
				:textElement(0)
				,textComposition(0)
				,editVersion(0)
				,textControl(0)
				,callback(0)
				,dragging(false)
				,readonly(false)
				,preventEnterDueToAutoComplete(false)
			{
				undoRedoProcessor=new GuiTextBoxUndoRedoProcessor;
				AttachTextEditCallback(undoRedoProcessor);

				internalShortcutKeyManager=new GuiShortcutKeyManager;
				AddShortcutCommand('Z', Func<bool()>(this, &GuiTextBoxCommonInterface::Undo));
				AddShortcutCommand('Y', Func<bool()>(this, &GuiTextBoxCommonInterface::Redo));
				AddShortcutCommand('A', Func<void()>(this, &GuiTextBoxCommonInterface::SelectAll));
				AddShortcutCommand('X', Func<bool()>(this, &GuiTextBoxCommonInterface::Cut));
				AddShortcutCommand('C', Func<bool()>(this, &GuiTextBoxCommonInterface::Copy));
				AddShortcutCommand('V', Func<bool()>(this, &GuiTextBoxCommonInterface::Paste));
			}

			GuiTextBoxCommonInterface::~GuiTextBoxCommonInterface()
			{
				if(colorizer)
				{
					DetachTextEditCallback(colorizer);
					colorizer=0;
				}
				if(undoRedoProcessor)
				{
					DetachTextEditCallback(undoRedoProcessor);
					undoRedoProcessor=0;
				}

				for(vint i=0;i<textEditCallbacks.Count();i++)
				{
					textEditCallbacks[i]->Detach();
				}
				textEditCallbacks.Clear();
			}

			//================ clipboard operations

			bool GuiTextBoxCommonInterface::CanCut()
			{
				return !readonly && textElement->GetCaretBegin()!=textElement->GetCaretEnd() && textElement->GetPasswordChar()==L'\0';
			}

			bool GuiTextBoxCommonInterface::CanCopy()
			{
				return textElement->GetCaretBegin()!=textElement->GetCaretEnd() && textElement->GetPasswordChar()==L'\0';
			}

			bool GuiTextBoxCommonInterface::CanPaste()
			{
				return !readonly && GetCurrentController()->ClipboardService()->ContainsText() && textElement->GetPasswordChar()==L'\0';
			}

			bool GuiTextBoxCommonInterface::Cut()
			{
				if(CanCut())
				{
					GetCurrentController()->ClipboardService()->SetText(GetSelectionText());
					SetSelectionText(L"");
					return true;
				}
				else
				{
					return false;
				}
			}

			bool GuiTextBoxCommonInterface::Copy()
			{
				if(CanCopy())
				{
					GetCurrentController()->ClipboardService()->SetText(GetSelectionText());
					return true;
				}
				else
				{
					return false;
				}
			}

			bool GuiTextBoxCommonInterface::Paste()
			{
				if(CanPaste())
				{
					SetSelectionText(GetCurrentController()->ClipboardService()->GetText());
					return true;
				}
				else
				{
					return false;
				}
			}
			
			//================ editing control
			
			bool GuiTextBoxCommonInterface::GetReadonly()
			{
				return readonly;
			}

			void GuiTextBoxCommonInterface::SetReadonly(bool value)
			{
				readonly=value;
			}

			//================ text operations

			void GuiTextBoxCommonInterface::Select(TextPos begin, TextPos end)
			{
				Move(begin, false);
				Move(end, true);
			}

			void GuiTextBoxCommonInterface::SelectAll()
			{
				vint row=textElement->GetLines().GetCount()-1;
				Move(TextPos(0, 0), false);
				Move(TextPos(row, textElement->GetLines().GetLine(row).dataLength), true);
			}

			WString GuiTextBoxCommonInterface::GetSelectionText()
			{
				TextPos selectionBegin=textElement->GetCaretBegin()<textElement->GetCaretEnd()?textElement->GetCaretBegin():textElement->GetCaretEnd();
				TextPos selectionEnd=textElement->GetCaretBegin()>textElement->GetCaretEnd()?textElement->GetCaretBegin():textElement->GetCaretEnd();
				return textElement->GetLines().GetText(selectionBegin, selectionEnd);
			}

			void GuiTextBoxCommonInterface::SetSelectionText(const WString& value)
			{
				Modify(textElement->GetCaretBegin(), textElement->GetCaretEnd(), value, false);
			}

			void GuiTextBoxCommonInterface::SetSelectionTextAsKeyInput(const WString& value)
			{
				Modify(textElement->GetCaretBegin(), textElement->GetCaretEnd(), value, true);
			}

			WString GuiTextBoxCommonInterface::GetRowText(vint row)
			{
				TextPos start=textElement->GetLines().Normalize(TextPos(row, 0));
				TextPos end=TextPos(start.row, textElement->GetLines().GetLine(start.row).dataLength);
				return GetFragmentText(start, end);
			}

			vint GuiTextBoxCommonInterface::GetRowCount()
			{
				return textElement->GetLines().GetCount();
			}

			WString GuiTextBoxCommonInterface::GetFragmentText(TextPos start, TextPos end)
			{
				start=textElement->GetLines().Normalize(start);
				end=textElement->GetLines().Normalize(end);
				return textElement->GetLines().GetText(start, end);
			}

			TextPos GuiTextBoxCommonInterface::GetCaretBegin()
			{
				return textElement->GetCaretBegin();
			}

			TextPos GuiTextBoxCommonInterface::GetCaretEnd()
			{
				return textElement->GetCaretEnd();
			}

			TextPos GuiTextBoxCommonInterface::GetCaretSmall()
			{
				TextPos c1=GetCaretBegin();
				TextPos c2=GetCaretEnd();
				return c1<c2?c1:c2;
			}

			TextPos GuiTextBoxCommonInterface::GetCaretLarge()
			{
				TextPos c1=GetCaretBegin();
				TextPos c2=GetCaretEnd();
				return c1>c2?c1:c2;
			}

			//================ position query
				
			vint GuiTextBoxCommonInterface::GetRowWidth(vint row)
			{
				return textElement->GetLines().GetRowWidth(row);
			}

			vint GuiTextBoxCommonInterface::GetRowHeight()
			{
				return textElement->GetLines().GetRowHeight();
			}

			vint GuiTextBoxCommonInterface::GetMaxWidth()
			{
				return textElement->GetLines().GetMaxWidth();
			}

			vint GuiTextBoxCommonInterface::GetMaxHeight()
			{
				return textElement->GetLines().GetMaxHeight();
			}

			TextPos GuiTextBoxCommonInterface::GetTextPosFromPoint(Point point)
			{
				Point view=textElement->GetViewPosition();
				return textElement->GetLines().GetTextPosFromPoint(Point(point.x+view.x, point.y+view.y));
			}

			Point GuiTextBoxCommonInterface::GetPointFromTextPos(TextPos pos)
			{
				Point view=textElement->GetViewPosition();
				Point result=textElement->GetLines().GetPointFromTextPos(pos);
				return Point(result.x-view.x, result.y-view.y);
			}

			Rect GuiTextBoxCommonInterface::GetRectFromTextPos(TextPos pos)
			{
				Point view=textElement->GetViewPosition();
				Rect result=textElement->GetLines().GetRectFromTextPos(pos);
				return Rect(Point(result.x1-view.x, result.y1-view.y), result.GetSize());
			}

			TextPos GuiTextBoxCommonInterface::GetNearestTextPos(Point point)
			{
				Point viewPosition=textElement->GetViewPosition();
				Point mousePosition=Point(point.x+viewPosition.x, point.y+viewPosition.y);
				TextPos pos=textElement->GetLines().GetTextPosFromPoint(mousePosition);
				if(pos.column<textElement->GetLines().GetLine(pos.row).dataLength)
				{
					Rect rect=textElement->GetLines().GetRectFromTextPos(pos);
					if(abs((int)(rect.x1-mousePosition.x))>=abs((int)(rect.x2-1-mousePosition.x)))
					{
						pos.column++;
					}
				}
				return pos;
			}

			//================ colorizing

			Ptr<GuiTextBoxColorizerBase> GuiTextBoxCommonInterface::GetColorizer()
			{
				return colorizer;
			}

			void GuiTextBoxCommonInterface::SetColorizer(Ptr<GuiTextBoxColorizerBase> value)
			{
				if (!filledDefaultColors)
				{
					filledDefaultColors = true;
					CopyFrom(defaultColors, GetTextElement()->GetColors());
				}

				if(colorizer)
				{
					DetachTextEditCallback(colorizer);
				}
				colorizer=value;
				if(colorizer)
				{
					AttachTextEditCallback(colorizer);
					GetTextElement()->SetColors(colorizer->GetColors());
				}
				else
				{
					GetTextElement()->SetColors(defaultColors);
					GetTextElement()->ResetTextColorIndex(0);
				}
			}

			//================ auto complete

			Ptr<GuiTextBoxAutoCompleteBase> GuiTextBoxCommonInterface::GetAutoComplete()
			{
				return autoComplete;
			}

			void GuiTextBoxCommonInterface::SetAutoComplete(Ptr<GuiTextBoxAutoCompleteBase> value)
			{
				if(autoComplete)
				{
					DetachTextEditCallback(autoComplete);
				}
				autoComplete=value;
				if(autoComplete)
				{
					AttachTextEditCallback(autoComplete);
				}
			}

			//================ undo redo control

			vuint GuiTextBoxCommonInterface::GetEditVersion()
			{
				return editVersion;
			}

			bool GuiTextBoxCommonInterface::CanUndo()
			{
				return !readonly && undoRedoProcessor->CanUndo();
			}

			bool GuiTextBoxCommonInterface::CanRedo()
			{
				return !readonly && undoRedoProcessor->CanRedo();
			}

			void GuiTextBoxCommonInterface::ClearUndoRedo()
			{
				undoRedoProcessor->ClearUndoRedo();
			}

			bool GuiTextBoxCommonInterface::GetModified()
			{
				return undoRedoProcessor->GetModified();
			}

			void GuiTextBoxCommonInterface::NotifyModificationSaved()
			{
				undoRedoProcessor->NotifyModificationSaved();
			}

			bool GuiTextBoxCommonInterface::Undo()
			{
				if(CanUndo())
				{
					return undoRedoProcessor->Undo();
				}
				else
				{
					return false;
				}
			}

			bool GuiTextBoxCommonInterface::Redo()
			{
				if(CanRedo())
				{
					return undoRedoProcessor->Redo();
				}
				else
				{
					return false;
				}
			}
		}
	}
}

/***********************************************************************
.\CONTROLS\TEXTEDITORPACKAGE\GUITEXTCONTROLS.CPP
***********************************************************************/

namespace vl
{
	namespace presentation
	{
		namespace controls
		{
			using namespace elements;
			using namespace elements::text;
			using namespace compositions;
			using namespace collections;

/***********************************************************************
GuiMultilineTextBox::DefaultTextElementOperatorCallback
***********************************************************************/

			GuiMultilineTextBox::TextElementOperatorCallback::TextElementOperatorCallback(GuiMultilineTextBox* _textControl)
				:GuiTextBoxCommonInterface::DefaultCallback(
					_textControl->textElement,
					_textControl->textComposition
					)
				,textControl(_textControl)
			{
			}

			void GuiMultilineTextBox::TextElementOperatorCallback::AfterModify(TextPos originalStart, TextPos originalEnd, const WString& originalText, TextPos inputStart, TextPos inputEnd, const WString& inputText)
			{
				textControl->CalculateView();
			}
			
			void GuiMultilineTextBox::TextElementOperatorCallback::ScrollToView(Point point)
			{
				point.x+=TextMargin;
				point.y+=TextMargin;
				Point oldPoint = textControl->GetViewPosition();
				vint marginX=0;
				vint marginY=0;
				if(oldPoint.x<point.x)
				{
					marginX=TextMargin;
				}
				else if(oldPoint.x>point.x)
				{
					marginX=-TextMargin;
				}
				if(oldPoint.y<point.y)
				{
					marginY=TextMargin;
				}
				else if(oldPoint.y>point.y)
				{
					marginY=-TextMargin;
				}
				textControl->SetViewPosition(Point(point.x + marginX, point.y + marginY));
			}

			vint GuiMultilineTextBox::TextElementOperatorCallback::GetTextMargin()
			{
				return TextMargin;
			}

/***********************************************************************
GuiMultilineTextBox::CommandExecutor
***********************************************************************/

			GuiMultilineTextBox::CommandExecutor::CommandExecutor(GuiMultilineTextBox* _textBox)
				:textBox(_textBox)
			{
			}

			GuiMultilineTextBox::CommandExecutor::~CommandExecutor()
			{
			}

			void GuiMultilineTextBox::CommandExecutor::UnsafeSetText(const WString& value)
			{
				textBox->UnsafeSetText(value);
			}

/***********************************************************************
GuiMultilineTextBox
***********************************************************************/

			void GuiMultilineTextBox::BeforeControlTemplateUninstalled_()
			{
				auto ct = GetControlTemplateObject(false);
				if (!ct) return;

				ct->SetCommands(nullptr);
			}

			void GuiMultilineTextBox::AfterControlTemplateInstalled_(bool initialize)
			{
				auto ct = GetControlTemplateObject(true);
				Array<text::ColorEntry> colors(1);
				colors[0] = ct->GetTextColor();
				textElement->SetColors(colors);
				textElement->SetCaretColor(ct->GetCaretColor());
				ct->SetCommands(commandExecutor.Obj());
			}

			void GuiMultilineTextBox::CalculateViewAndSetScroll()
			{
				auto ct = GetControlTemplateObject(true);
				CalculateView();
				vint smallMove = textElement->GetLines().GetRowHeight();
				vint bigMove = smallMove * 5;

				if (auto scroll = ct->GetHorizontalScroll())
				{
					scroll->SetSmallMove(smallMove);
					scroll->SetBigMove(bigMove);
				}

				if (auto scroll = ct->GetVerticalScroll())
				{
					scroll->SetSmallMove(smallMove);
					scroll->SetBigMove(bigMove);
				}
			}

			void GuiMultilineTextBox::OnVisuallyEnabledChanged(compositions::GuiGraphicsComposition* sender, compositions::GuiEventArgs& arguments)
			{
				textElement->SetVisuallyEnabled(GetVisuallyEnabled());
			}

			Size GuiMultilineTextBox::QueryFullSize()
			{
				text::TextLines& lines = textElement->GetLines();
				return Size(lines.GetMaxWidth() + TextMargin * 2, lines.GetMaxHeight() + TextMargin * 2);
			}

			void GuiMultilineTextBox::UpdateView(Rect viewBounds)
			{
				textElement->SetViewPosition(viewBounds.LeftTop() - Size(TextMargin, TextMargin));
			}

			void GuiMultilineTextBox::OnRenderTargetChanged(elements::IGuiGraphicsRenderTarget* renderTarget)
			{
				CalculateViewAndSetScroll();
				GuiScrollView::OnRenderTargetChanged(renderTarget);
			}

			void GuiMultilineTextBox::OnBoundsMouseButtonDown(compositions::GuiGraphicsComposition* sender, compositions::GuiMouseEventArgs& arguments)
			{
				if(GetVisuallyEnabled())
				{
					boundsComposition->GetRelatedGraphicsHost()->SetFocus(boundsComposition);
				}
			}

			GuiMultilineTextBox::GuiMultilineTextBox(theme::ThemeName themeName)
				:GuiScrollView(themeName)
			{
				textElement = GuiColorizedTextElement::Create();
				textElement->SetFont(GetFont());

				textComposition = new GuiBoundsComposition;
				textComposition->SetAlignmentToParent(Margin(0, 0, 0, 0));
				textComposition->SetOwnedElement(textElement);
				containerComposition->AddChild(textComposition);

				callback = new TextElementOperatorCallback(this);
				commandExecutor = new CommandExecutor(this);

				SetFocusableComposition(boundsComposition);
				Install(textElement, textComposition, this, boundsComposition, focusableComposition);
				SetCallback(callback.Obj());

				VisuallyEnabledChanged.AttachMethod(this, &GuiMultilineTextBox::OnVisuallyEnabledChanged);
				boundsComposition->GetEventReceiver()->leftButtonDown.AttachMethod(this, &GuiMultilineTextBox::OnBoundsMouseButtonDown);
				boundsComposition->GetEventReceiver()->middleButtonDown.AttachMethod(this, &GuiMultilineTextBox::OnBoundsMouseButtonDown);
				boundsComposition->GetEventReceiver()->rightButtonDown.AttachMethod(this, &GuiMultilineTextBox::OnBoundsMouseButtonDown);
			}

			GuiMultilineTextBox::~GuiMultilineTextBox()
			{
			}

			const WString& GuiMultilineTextBox::GetText()
			{
				text = textElement->GetLines().GetText();
				return text;
			}

			void GuiMultilineTextBox::SetText(const WString& value)
			{
				UnsafeSetText(value);
				textElement->SetCaretBegin(TextPos(0, 0));
				textElement->SetCaretEnd(TextPos(0, 0));
				CalculateView();
			}

			void GuiMultilineTextBox::SetFont(const FontProperties& value)
			{
				GuiControl::SetFont(value);
				textElement->SetFont(value);
				CalculateViewAndSetScroll();
			}

/***********************************************************************
GuiSinglelineTextBox::DefaultTextElementOperatorCallback
***********************************************************************/

			GuiSinglelineTextBox::TextElementOperatorCallback::TextElementOperatorCallback(GuiSinglelineTextBox* _textControl)
				:GuiTextBoxCommonInterface::DefaultCallback(
					_textControl->textElement,
					_textControl->textComposition
					)
			{
			}

			bool GuiSinglelineTextBox::TextElementOperatorCallback::BeforeModify(TextPos start, TextPos end, const WString& originalText, WString& inputText)
			{
				vint length=inputText.Length();
				const wchar_t* input=inputText.Buffer();
				for(vint i=0;i<length;i++)
				{
					if(*input==0 || *input==L'\r' || *input==L'\n')
					{
						length=i;
						break;
					}
				}
				if(length!=inputText.Length())
				{
					inputText=inputText.Left(length);
				}
				return true;
			}

			void GuiSinglelineTextBox::TextElementOperatorCallback::AfterModify(TextPos originalStart, TextPos originalEnd, const WString& originalText, TextPos inputStart, TextPos inputEnd, const WString& inputText)
			{
			}
			
			void GuiSinglelineTextBox::TextElementOperatorCallback::ScrollToView(Point point)
			{
				vint newX=point.x;
				vint oldX=textElement->GetViewPosition().x;
				vint marginX=0;
				if(oldX<newX)
				{
					marginX=TextMargin;
				}
				else if(oldX>newX)
				{
					marginX=-TextMargin;
				}

				newX+=marginX;
				vint minX=-TextMargin;
				vint maxX=textElement->GetLines().GetMaxWidth()+TextMargin-textComposition->GetBounds().Width();
				if(newX>=maxX)
				{
					newX=maxX-1;
				}
				if(newX<minX)
				{
					newX=minX;
				}
				textElement->SetViewPosition(Point(newX, -TextMargin));
			}

			vint GuiSinglelineTextBox::TextElementOperatorCallback::GetTextMargin()
			{
				return TextMargin;
			}

/***********************************************************************
GuiSinglelineTextBox
***********************************************************************/

			void GuiSinglelineTextBox::BeforeControlTemplateUninstalled_()
			{
			}

			void GuiSinglelineTextBox::AfterControlTemplateInstalled_(bool initialize)
			{
				auto ct = GetControlTemplateObject(true);
				Array<text::ColorEntry> colors(1);
				colors[0] = ct->GetTextColor();
				textElement->SetColors(colors);
				textElement->SetCaretColor(ct->GetCaretColor());
			}

			void GuiSinglelineTextBox::RearrangeTextElement()
			{
				textCompositionTable->SetRowOption(
					1,
					GuiCellOption::AbsoluteOption(
						textElement->GetLines().GetRowHeight() + 2 * TextMargin)
				);
			}

			void GuiSinglelineTextBox::OnRenderTargetChanged(elements::IGuiGraphicsRenderTarget* renderTarget)
			{
				GuiControl::OnRenderTargetChanged(renderTarget);
				RearrangeTextElement();
			}

			void GuiSinglelineTextBox::OnVisuallyEnabledChanged(compositions::GuiGraphicsComposition* sender, compositions::GuiEventArgs& arguments)
			{
				textElement->SetVisuallyEnabled(GetVisuallyEnabled());
			}

			void GuiSinglelineTextBox::OnBoundsMouseButtonDown(compositions::GuiGraphicsComposition* sender, compositions::GuiMouseEventArgs& arguments)
			{
				if(GetVisuallyEnabled())
				{
					boundsComposition->GetRelatedGraphicsHost()->SetFocus(boundsComposition);
				}
			}

			GuiSinglelineTextBox::GuiSinglelineTextBox(theme::ThemeName themeName)
				:GuiControl(themeName)
			{
				textElement = GuiColorizedTextElement::Create();
				textElement->SetFont(GetFont());
				textElement->SetViewPosition(Point(-GuiSinglelineTextBox::TextMargin, -GuiSinglelineTextBox::TextMargin));

				textCompositionTable = new GuiTableComposition;
				textCompositionTable->SetMinSizeLimitation(GuiGraphicsComposition::LimitToElementAndChildren);
				textCompositionTable->SetAlignmentToParent(Margin(0, 0, 0, 0));
				textCompositionTable->SetRowsAndColumns(3, 1);
				textCompositionTable->SetRowOption(0, GuiCellOption::PercentageOption(0.5));
				textCompositionTable->SetRowOption(1, GuiCellOption::AbsoluteOption(0));
				textCompositionTable->SetRowOption(2, GuiCellOption::PercentageOption(0.5));
				textCompositionTable->SetColumnOption(0, GuiCellOption::PercentageOption(1.0));
				containerComposition->AddChild(textCompositionTable);

				textComposition = new GuiCellComposition;
				textComposition->SetOwnedElement(textElement);
				textCompositionTable->AddChild(textComposition);
				textComposition->SetSite(1, 0, 1, 1);

				callback = new TextElementOperatorCallback(this);
				SetFocusableComposition(boundsComposition);
				Install(textElement, textComposition, this, boundsComposition, focusableComposition);
				SetCallback(callback.Obj());

				VisuallyEnabledChanged.AttachMethod(this, &GuiSinglelineTextBox::OnVisuallyEnabledChanged);
				boundsComposition->GetEventReceiver()->leftButtonDown.AttachMethod(this, &GuiSinglelineTextBox::OnBoundsMouseButtonDown);
				boundsComposition->GetEventReceiver()->middleButtonDown.AttachMethod(this, &GuiSinglelineTextBox::OnBoundsMouseButtonDown);
				boundsComposition->GetEventReceiver()->rightButtonDown.AttachMethod(this, &GuiSinglelineTextBox::OnBoundsMouseButtonDown);
			}

			GuiSinglelineTextBox::~GuiSinglelineTextBox()
			{
			}

			const WString& GuiSinglelineTextBox::GetText()
			{
				text = textElement->GetLines().GetText();
				return text;
			}

			void GuiSinglelineTextBox::SetText(const WString& value)
			{
				UnsafeSetText(value);
				textElement->SetCaretBegin(TextPos(0, 0));
				textElement->SetCaretEnd(TextPos(0, 0));
			}

			void GuiSinglelineTextBox::SetFont(const FontProperties& value)
			{
				GuiControl::SetFont(value);
				textElement->SetFont(value);
				RearrangeTextElement();
			}

			wchar_t GuiSinglelineTextBox::GetPasswordChar()
			{
				return textElement->GetPasswordChar();
			}

			void GuiSinglelineTextBox::SetPasswordChar(wchar_t value)
			{
				textElement->SetPasswordChar(value);
			}
		}
	}
}

/***********************************************************************
.\CONTROLS\TEMPLATES\GUIANIMATION.CPP
***********************************************************************/

namespace vl
{
	namespace presentation
	{
		namespace controls
		{
			using namespace collections;
			using namespace reflection::description;

/***********************************************************************
GuiTimedAnimation
***********************************************************************/

			class GuiTimedAnimation : public Object, public virtual IGuiAnimation
			{
			protected:
				DateTime						startTime;
				vuint64_t						time;
				bool							running = false;

			public:
				GuiTimedAnimation()
				{
				}

				~GuiTimedAnimation()
				{
				}

				void Start()override
				{
					startTime = DateTime::LocalTime();
					time = 0;
					running = true;
				}

				void Pause()override
				{
					time = GetTime();
					running = false;
				}

				void Resume()override
				{
					startTime = DateTime::LocalTime();
					running = true;
				}

				vuint64_t GetTime()
				{
					if (running)
					{
						return time + (DateTime::LocalTime().totalMilliseconds - startTime.totalMilliseconds);
					}
					else
					{
						return time;
					}
				}
			};

/***********************************************************************
GuiFiniteAnimation
***********************************************************************/

			class GuiFiniteAnimation : public GuiTimedAnimation
			{
			protected:
				vuint64_t						length = 0;
				Func<void(vuint64_t)>			run;

			public:
				GuiFiniteAnimation(const Func<void(vuint64_t)>& _run, vuint64_t _length)
					:run(_run)
					, length(_length)
				{
				}

				~GuiFiniteAnimation()
				{
				}

				void Run()override
				{
					auto currentTime = GetTime();
					if (currentTime < length && run)
					{
						run(currentTime);
					}
				}

				bool GetStopped()override
				{
					return GetTime() >= length;
				}
			};

/***********************************************************************
GuiInfiniteAnimation
***********************************************************************/

			class GuiInfiniteAnimation : public GuiTimedAnimation
			{
			protected:
				Func<void(vuint64_t)>			run;

			public:
				GuiInfiniteAnimation(const Func<void(vuint64_t)>& _run)
					:run(_run)
				{
				}

				~GuiInfiniteAnimation()
				{
				}

				void Run()override
				{
					if (run)
					{
						run(GetTime());
					}
				}

				bool GetStopped()override
				{
					return false;
				}
			};

/***********************************************************************
IGuiAnimation
***********************************************************************/

			Ptr<IGuiAnimation> IGuiAnimation::CreateAnimation(const Func<void(vuint64_t)>& run, vuint64_t milliseconds)
			{
				return new GuiFiniteAnimation(run, milliseconds);
			}

			Ptr<IGuiAnimation> IGuiAnimation::CreateAnimation(const Func<void(vuint64_t)>& run)
			{
				return new GuiInfiniteAnimation(run);
			}

/***********************************************************************
IGuiAnimationCoroutine
***********************************************************************/

			class GuiCoroutineAnimation : public Object, public virtual IGuiAnimationCoroutine::IImpl
			{
			protected:
				IGuiAnimationCoroutine::Creator				creator;
				Ptr<ICoroutine>								coroutine;

				Ptr<IGuiAnimation>							waitingAnimation;
				vint										waitingGroup = -1;
				Group<vint, Ptr<IGuiAnimation>>				groupAnimations;

			public:
				GuiCoroutineAnimation(const IGuiAnimationCoroutine::Creator& _creator)
					:creator(_creator)
				{
				}

				~GuiCoroutineAnimation()
				{
				}

				void OnPlayAndWait(Ptr<IGuiAnimation> animation)override
				{
					CHECK_ERROR(!waitingAnimation && waitingGroup == -1, L"GuiCoroutineAnimation::OnPlayAndWait(Ptr<IGuiAnimation>)#Cannot be called when an animation or a group has already been waiting for.");
					waitingAnimation = animation;
					waitingAnimation->Start();
				}

				void OnPlayInGroup(Ptr<IGuiAnimation> animation, vint groupId)override
				{
					groupAnimations.Add(groupId, animation);
					animation->Start();
				}

				void OnWaitForGroup(vint groupId)override
				{
					CHECK_ERROR(!waitingAnimation && waitingGroup == -1, L"GuiCoroutineAnimation::OnWaitForGroup(vint)#Cannot be called when an animation or a group has already been waiting for.");
					if (groupAnimations.Keys().Contains(groupId))
					{
						waitingGroup = groupId;
					}
				}

				void Start()override
				{
					CHECK_ERROR(!coroutine, L"GuiCoroutineAnimation::Start()#Cannot be called more than once.");
					coroutine = creator(this);
				}

				void Pause()override
				{
					if (waitingAnimation)
					{
						waitingAnimation->Pause();
					}
					for (vint i = 0; i < groupAnimations.Count(); i++)
					{
						FOREACH(Ptr<IGuiAnimation>, animation, groupAnimations.GetByIndex(i))
						{
							animation->Pause();
						}
					}
				}

				void Resume()override
				{
					if (waitingAnimation)
					{
						waitingAnimation->Resume();
					}
					for (vint i = 0; i < groupAnimations.Count(); i++)
					{
						FOREACH(Ptr<IGuiAnimation>, animation, groupAnimations.GetByIndex(i))
						{
							animation->Resume();
						}
					}
				}

				void Run()override
				{
					CHECK_ERROR(coroutine, L"GuiCoroutineAnimation::Run()#Cannot be called before calling Start.");

					if (waitingAnimation)
					{
						waitingAnimation->Run();
						if (waitingAnimation->GetStopped())
						{
							waitingAnimation = nullptr;
						}
					}

					for (vint i = groupAnimations.Count() - 1; i >= 0; i--)
					{
						auto& animations = groupAnimations.GetByIndex(i);
						for (vint j = animations.Count() - 1; j >= 0; j--)
						{
							auto animation = animations[j];
							animation->Run();
							if (animation->GetStopped())
							{
								groupAnimations.Remove(i, animation.Obj());
							}
						}
					}

					if (waitingGroup != -1 && !groupAnimations.Keys().Contains(waitingGroup))
					{
						waitingGroup = -1;
					}

					if (coroutine->GetStatus() == CoroutineStatus::Waiting)
					{
						if (waitingAnimation || waitingGroup != -1)
						{
							return;
						}
						coroutine->Resume(true, nullptr);
					}
				}

				bool GetStopped()override
				{
					if (!coroutine) return false;
					if (coroutine->GetStatus() != CoroutineStatus::Stopped) return false;
					if (waitingAnimation || groupAnimations.Count() > 0) return false;
					return true;
				}
			};

			void IGuiAnimationCoroutine::WaitAndPause(IImpl* impl, vuint64_t milliseconds)
			{
				return PlayAndWaitAndPause(impl, IGuiAnimation::CreateAnimation({}, milliseconds));
			}

			void IGuiAnimationCoroutine::PlayAndWaitAndPause(IImpl* impl, Ptr<IGuiAnimation> animation)
			{
				impl->OnPlayAndWait(animation);
			}

			void IGuiAnimationCoroutine::PlayInGroupAndPause(IImpl* impl, Ptr<IGuiAnimation> animation, vint groupId)
			{
				impl->OnPlayInGroup(animation, groupId);
			}

			void IGuiAnimationCoroutine::WaitForGroupAndPause(IImpl* impl, vint groupId)
			{
				impl->OnWaitForGroup(groupId);
			}

			void IGuiAnimationCoroutine::ReturnAndExit(IImpl* impl)
			{
			}

			Ptr<IGuiAnimation> IGuiAnimationCoroutine::Create(const Creator& creator)
			{
				return new GuiCoroutineAnimation(creator);
			}
		}
	}
}

/***********************************************************************
.\CONTROLS\TEXTEDITORPACKAGE\EDITORCALLBACK\GUITEXTUNDOREDO.CPP
***********************************************************************/

namespace vl
{
	namespace presentation
	{
		namespace controls
		{
			using namespace elements;
			using namespace elements::text;
			using namespace compositions;

/***********************************************************************
GuiGeneralUndoRedoProcessor
***********************************************************************/

			GuiGeneralUndoRedoProcessor::GuiGeneralUndoRedoProcessor()
				:firstFutureStep(0)
				,savedStep(0)
				,performingUndoRedo(false)
			{
			}

			GuiGeneralUndoRedoProcessor::~GuiGeneralUndoRedoProcessor()
			{
			}

			void GuiGeneralUndoRedoProcessor::PushStep(Ptr<IEditStep> step)
			{
				if(!performingUndoRedo)
				{
					if(firstFutureStep<savedStep)
					{
						savedStep=-1;
					}

					vint count=steps.Count()-firstFutureStep;
					if(count>0)
					{
						steps.RemoveRange(firstFutureStep, count);
					}
				
					steps.Add(step);
					firstFutureStep=steps.Count();
					UndoRedoChanged();
					ModifiedChanged();
				}
			}

			bool GuiGeneralUndoRedoProcessor::CanUndo()
			{
				return firstFutureStep>0;
			}

			bool GuiGeneralUndoRedoProcessor::CanRedo()
			{
				return steps.Count()>firstFutureStep;
			}

			void GuiGeneralUndoRedoProcessor::ClearUndoRedo()
			{
				if(!performingUndoRedo)
				{
					steps.Clear();
					firstFutureStep=0;
					savedStep=0;
				}
			}

			bool GuiGeneralUndoRedoProcessor::GetModified()
			{
				return firstFutureStep!=savedStep;
			}

			void GuiGeneralUndoRedoProcessor::NotifyModificationSaved()
			{
				if(!performingUndoRedo)
				{
					savedStep=firstFutureStep;
					ModifiedChanged();
				}
			}

			bool GuiGeneralUndoRedoProcessor::Undo()
			{
				if(!CanUndo()) return false;
				performingUndoRedo=true;
				firstFutureStep--;
				steps[firstFutureStep]->Undo();
				performingUndoRedo=false;
				UndoRedoChanged();
				ModifiedChanged();
				return true;
			}

			bool GuiGeneralUndoRedoProcessor::Redo()
			{
				if(!CanRedo()) return false;
				performingUndoRedo=true;
				firstFutureStep++;
				steps[firstFutureStep-1]->Redo();
				performingUndoRedo=false;
				UndoRedoChanged();
				ModifiedChanged();
				return true;
			}

/***********************************************************************
GuiTextBoxUndoRedoProcessor::EditStep
***********************************************************************/

			void GuiTextBoxUndoRedoProcessor::EditStep::Undo()
			{
				GuiTextBoxCommonInterface* ci=dynamic_cast<GuiTextBoxCommonInterface*>(processor->ownerComposition->GetRelatedControl());
				if(ci)
				{
					ci->Select(arguments.inputStart, arguments.inputEnd);
					ci->SetSelectionText(arguments.originalText);
					ci->Select(arguments.originalStart, arguments.originalEnd);
				}
			}

			void GuiTextBoxUndoRedoProcessor::EditStep::Redo()
			{
				GuiTextBoxCommonInterface* ci=dynamic_cast<GuiTextBoxCommonInterface*>(processor->ownerComposition->GetRelatedControl());
				if(ci)
				{
					ci->Select(arguments.originalStart, arguments.originalEnd);
					ci->SetSelectionText(arguments.inputText);
					ci->Select(arguments.inputStart, arguments.inputEnd);
				}
			}

/***********************************************************************
GuiTextBoxUndoRedoProcessor
***********************************************************************/

			GuiTextBoxUndoRedoProcessor::GuiTextBoxUndoRedoProcessor()
				:ownerComposition(0)
			{
			}

			GuiTextBoxUndoRedoProcessor::~GuiTextBoxUndoRedoProcessor()
			{
			}

			void GuiTextBoxUndoRedoProcessor::Attach(elements::GuiColorizedTextElement* element, SpinLock& elementModifyLock, compositions::GuiGraphicsComposition* _ownerComposition, vuint editVersion)
			{
				ownerComposition=_ownerComposition;
			}

			void GuiTextBoxUndoRedoProcessor::Detach()
			{
				ClearUndoRedo();
			}

			void GuiTextBoxUndoRedoProcessor::TextEditPreview(TextEditPreviewStruct& arguments)
			{
			}

			void GuiTextBoxUndoRedoProcessor::TextEditNotify(const TextEditNotifyStruct& arguments)
			{
				Ptr<EditStep> step=new EditStep;
				step->processor=this;
				step->arguments=arguments;
				PushStep(step);
			}

			void GuiTextBoxUndoRedoProcessor::TextCaretChanged(const TextCaretChangedStruct& arguments)
			{
			}

			void GuiTextBoxUndoRedoProcessor::TextEditFinished(vuint editVersion)
			{
			}

/***********************************************************************
GuiDocumentUndoRedoProcessor::ReplaceModelStep
***********************************************************************/

			void GuiDocumentUndoRedoProcessor::ReplaceModelStep::Undo()
			{
				GuiDocumentCommonInterface* ci=dynamic_cast<GuiDocumentCommonInterface*>(processor->ownerComposition->GetRelatedControl());
				if(ci)
				{
					ci->EditRun(arguments.inputStart, arguments.inputEnd, arguments.originalModel, true);
					ci->SetCaret(arguments.originalStart, arguments.originalEnd);
				}
			}

			void GuiDocumentUndoRedoProcessor::ReplaceModelStep::Redo()
			{
				GuiDocumentCommonInterface* ci=dynamic_cast<GuiDocumentCommonInterface*>(processor->ownerComposition->GetRelatedControl());
				if(ci)
				{
					ci->EditRun(arguments.originalStart, arguments.originalEnd, arguments.inputModel, true);
					ci->SetCaret(arguments.inputStart, arguments.inputEnd);
				}
			}

/***********************************************************************
GuiDocumentUndoRedoProcessor::RenameStyleStep
***********************************************************************/

			void GuiDocumentUndoRedoProcessor::RenameStyleStep::Undo()
			{
				GuiDocumentCommonInterface* ci=dynamic_cast<GuiDocumentCommonInterface*>(processor->ownerComposition->GetRelatedControl());
				if(ci)
				{
					ci->RenameStyle(arguments.newStyleName, arguments.oldStyleName);
				}
			}

			void GuiDocumentUndoRedoProcessor::RenameStyleStep::Redo()
			{
				GuiDocumentCommonInterface* ci=dynamic_cast<GuiDocumentCommonInterface*>(processor->ownerComposition->GetRelatedControl());
				if(ci)
				{
					ci->RenameStyle(arguments.oldStyleName, arguments.newStyleName);
				}
			}

/***********************************************************************
GuiDocumentUndoRedoProcessor::SetAlignmentStep
***********************************************************************/

			void GuiDocumentUndoRedoProcessor::SetAlignmentStep::Undo()
			{
				GuiDocumentCommonInterface* ci=dynamic_cast<GuiDocumentCommonInterface*>(processor->ownerComposition->GetRelatedControl());
				if(ci)
				{
					ci->SetParagraphAlignments(TextPos(arguments->start, 0), TextPos(arguments->end, 0), arguments->originalAlignments);
				}
			}

			void GuiDocumentUndoRedoProcessor::SetAlignmentStep::Redo()
			{
				GuiDocumentCommonInterface* ci=dynamic_cast<GuiDocumentCommonInterface*>(processor->ownerComposition->GetRelatedControl());
				if(ci)
				{
					ci->SetParagraphAlignments(TextPos(arguments->start, 0), TextPos(arguments->end, 0), arguments->inputAlignments);
				}
			}

/***********************************************************************
GuiDocumentUndoRedoProcessor
***********************************************************************/

			GuiDocumentUndoRedoProcessor::GuiDocumentUndoRedoProcessor()
				:element(0)
				,ownerComposition(0)
			{
			}

			GuiDocumentUndoRedoProcessor::~GuiDocumentUndoRedoProcessor()
			{
			}

			void GuiDocumentUndoRedoProcessor::Setup(elements::GuiDocumentElement* _element, compositions::GuiGraphicsComposition* _ownerComposition)
			{
				element=_element;
				ownerComposition=_ownerComposition;
			}

			void GuiDocumentUndoRedoProcessor::OnReplaceModel(const ReplaceModelStruct& arguments)
			{
				Ptr<ReplaceModelStep> step=new ReplaceModelStep;
				step->processor=this;
				step->arguments=arguments;
				PushStep(step);
			}

			void GuiDocumentUndoRedoProcessor::OnRenameStyle(const RenameStyleStruct& arguments)
			{
				Ptr<RenameStyleStep> step=new RenameStyleStep;
				step->processor=this;
				step->arguments=arguments;
				PushStep(step);
			}

			void GuiDocumentUndoRedoProcessor::OnSetAlignment(Ptr<SetAlignmentStruct> arguments)
			{
				Ptr<SetAlignmentStep> step=new SetAlignmentStep;
				step->processor=this;
				step->arguments=arguments;
				PushStep(step);
			}
		}
	}
}

/***********************************************************************
.\CONTROLS\TEXTEDITORPACKAGE\GUIDOCUMENTVIEWER.CPP
***********************************************************************/

namespace vl
{
	namespace presentation
	{
		namespace controls
		{
			using namespace collections;
			using namespace elements;
			using namespace compositions;

/***********************************************************************
GuiDocumentItem
***********************************************************************/

			GuiDocumentItem::GuiDocumentItem(const WString& _name)
				:name(_name)
			{
				container = new GuiBoundsComposition;
				container->SetMinSizeLimitation(GuiGraphicsComposition::LimitToElementAndChildren);
				container->SetAssociatedCursor(GetCurrentController()->ResourceService()->GetSystemCursor(INativeCursor::Arrow));
			}

			GuiDocumentItem::~GuiDocumentItem()
			{
				if (!owned)
				{
					SafeDeleteComposition(container);
				}
			}

			compositions::GuiGraphicsComposition* GuiDocumentItem::GetContainer()
			{
				return container;
			}

			WString GuiDocumentItem::GetName()
			{
				return name;
			}

/***********************************************************************
GuiDocumentCommonInterface
***********************************************************************/
			
			void GuiDocumentCommonInterface::InvokeUndoRedoChanged()
			{
				UndoRedoChanged.Execute(documentControl->GetNotifyEventArguments());
			}

			void GuiDocumentCommonInterface::InvokeModifiedChanged()
			{
				ModifiedChanged.Execute(documentControl->GetNotifyEventArguments());
			}

			void GuiDocumentCommonInterface::UpdateCaretPoint()
			{
				GuiGraphicsHost* host=documentComposition->GetRelatedGraphicsHost();
				if(host)
				{
					Rect caret=documentElement->GetCaretBounds(documentElement->GetCaretEnd(), documentElement->IsCaretEndPreferFrontSide());
					Point view=GetDocumentViewPosition();
					vint x=caret.x1-view.x;
					vint y=caret.y2-view.y;
					host->SetCaretPoint(Point(x, y), documentComposition);
				}
			}

			void GuiDocumentCommonInterface::Move(TextPos caret, bool shift, bool frontSide)
			{
				TextPos begin=documentElement->GetCaretBegin();
				TextPos end=documentElement->GetCaretEnd();
				
				TextPos newBegin=shift?begin:caret;
				TextPos newEnd=caret;
				documentElement->SetCaret(newBegin, newEnd, frontSide);
				documentElement->SetCaretVisible(true);

				Rect bounds=documentElement->GetCaretBounds(newEnd, frontSide);
				if(bounds!=Rect())
				{
					bounds.x1-=15;
					bounds.y1-=15;
					bounds.x2+=15;
					bounds.y2+=15;
					EnsureRectVisible(bounds);
				}
				UpdateCaretPoint();
				SelectionChanged.Execute(documentControl->GetNotifyEventArguments());
			}

			bool GuiDocumentCommonInterface::ProcessKey(vint code, bool shift, bool ctrl)
			{
				if(IGuiShortcutKeyItem* item=internalShortcutKeyManager->TryGetShortcut(ctrl, shift, false, code))
				{
					GuiEventArgs arguments;
					item->Executed.Execute(arguments);
					return true;
				}

				TextPos currentCaret=documentElement->GetCaretEnd();
				bool frontSide=documentElement->IsCaretEndPreferFrontSide();
				TextPos begin=documentElement->GetCaretBegin();
				TextPos end=documentElement->GetCaretEnd();

				switch(code)
				{
				case VKEY_UP:
					{
						TextPos newCaret=documentElement->CalculateCaret(currentCaret, IGuiGraphicsParagraph::CaretMoveUp, frontSide);
						Move(newCaret, shift, frontSide);
					}
					break;
				case VKEY_DOWN:
					{
						TextPos newCaret=documentElement->CalculateCaret(currentCaret, IGuiGraphicsParagraph::CaretMoveDown, frontSide);
						Move(newCaret, shift, frontSide);
					}
					break;
				case VKEY_LEFT:
					{
						TextPos newCaret=documentElement->CalculateCaret(currentCaret, IGuiGraphicsParagraph::CaretMoveLeft, frontSide);
						Move(newCaret, shift, frontSide);
					}
					break;
				case VKEY_RIGHT:
					{
						TextPos newCaret=documentElement->CalculateCaret(currentCaret, IGuiGraphicsParagraph::CaretMoveRight, frontSide);
						Move(newCaret, shift, frontSide);
					}
					break;
				case VKEY_HOME:
					{
						TextPos newCaret=documentElement->CalculateCaret(currentCaret, IGuiGraphicsParagraph::CaretLineFirst, frontSide);
						if(newCaret==currentCaret)
						{
							newCaret=documentElement->CalculateCaret(currentCaret, IGuiGraphicsParagraph::CaretFirst, frontSide);
						}
						Move(newCaret, shift, frontSide);
					}
					break;
				case VKEY_END:
					{
						TextPos newCaret=documentElement->CalculateCaret(currentCaret, IGuiGraphicsParagraph::CaretLineLast, frontSide);
						if(newCaret==currentCaret)
						{
							newCaret=documentElement->CalculateCaret(currentCaret, IGuiGraphicsParagraph::CaretLast, frontSide);
						}
						Move(newCaret, shift, frontSide);
					}
					break;
				case VKEY_PRIOR:
					{
					}
					break;
				case VKEY_NEXT:
					{
					}
					break;
				case VKEY_BACK:
					if(editMode==Editable)
					{
						if(begin==end)
						{
							ProcessKey(VKEY_LEFT, true, false);
						}
						Array<WString> text;
						EditText(documentElement->GetCaretBegin(), documentElement->GetCaretEnd(), documentElement->IsCaretEndPreferFrontSide(), text);
						return true;
					}
					break;
				case VKEY_DELETE:
					if(editMode==Editable)
					{
						if(begin==end)
						{
							ProcessKey(VKEY_RIGHT, true, false);
						}
						Array<WString> text;
						EditText(documentElement->GetCaretBegin(), documentElement->GetCaretEnd(), documentElement->IsCaretEndPreferFrontSide(), text);
						return true;
					}
					break;
				case VKEY_RETURN:
					if(editMode==Editable)
					{
						if(ctrl)
						{
							Array<WString> text(1);
							text[0]=L"\r\n";
							EditText(documentElement->GetCaretBegin(), documentElement->GetCaretEnd(), documentElement->IsCaretEndPreferFrontSide(), text);
						}
						else
						{
							Array<WString> text(2);
							EditText(documentElement->GetCaretBegin(), documentElement->GetCaretEnd(), documentElement->IsCaretEndPreferFrontSide(), text);
						}
						return true;
					}
					break;
				}
				return false;
			}

			void GuiDocumentCommonInterface::InstallDocumentViewer(
				GuiControl* _sender,
				compositions::GuiGraphicsComposition* _container,
				compositions::GuiGraphicsComposition* eventComposition,
				compositions::GuiGraphicsComposition* focusableComposition
				)
			{
				documentControl=_sender;

				documentElement=GuiDocumentElement::Create();
				documentElement->SetCallback(this);

				documentComposition=new GuiBoundsComposition;
				documentComposition->SetOwnedElement(documentElement);
				documentComposition->SetMinSizeLimitation(GuiGraphicsComposition::LimitToElement);
				documentComposition->SetAlignmentToParent(Margin(5, 5, 5, 5));
				_container->AddChild(documentComposition);

				documentComposition->GetEventReceiver()->mouseMove.AttachMethod(this, &GuiDocumentCommonInterface::OnMouseMove);
				documentComposition->GetEventReceiver()->leftButtonDown.AttachMethod(this, &GuiDocumentCommonInterface::OnMouseDown);
				documentComposition->GetEventReceiver()->leftButtonUp.AttachMethod(this, &GuiDocumentCommonInterface::OnMouseUp);
				documentComposition->GetEventReceiver()->mouseLeave.AttachMethod(this, &GuiDocumentCommonInterface::OnMouseLeave);

				_sender->FontChanged.AttachMethod(this, &GuiDocumentCommonInterface::OnFontChanged);
				focusableComposition->GetEventReceiver()->caretNotify.AttachMethod(this, &GuiDocumentCommonInterface::OnCaretNotify);
				focusableComposition->GetEventReceiver()->gotFocus.AttachMethod(this, &GuiDocumentCommonInterface::OnGotFocus);
				focusableComposition->GetEventReceiver()->lostFocus.AttachMethod(this, &GuiDocumentCommonInterface::OnLostFocus);
				focusableComposition->GetEventReceiver()->keyDown.AttachMethod(this, &GuiDocumentCommonInterface::OnKeyDown);
				focusableComposition->GetEventReceiver()->charInput.AttachMethod(this, &GuiDocumentCommonInterface::OnCharInput);

				undoRedoProcessor->Setup(documentElement, documentComposition);
				ActiveHyperlinkChanged.SetAssociatedComposition(eventComposition);
				ActiveHyperlinkExecuted.SetAssociatedComposition(eventComposition);
				SelectionChanged.SetAssociatedComposition(eventComposition);
				UndoRedoChanged.SetAssociatedComposition(eventComposition);
				ModifiedChanged.SetAssociatedComposition(eventComposition);

				undoRedoProcessor->UndoRedoChanged.Add(this, &GuiDocumentCommonInterface::InvokeUndoRedoChanged);
				undoRedoProcessor->ModifiedChanged.Add(this, &GuiDocumentCommonInterface::InvokeModifiedChanged);
				SetDocument(new DocumentModel);
			}

			void GuiDocumentCommonInterface::SetActiveHyperlink(Ptr<DocumentHyperlinkRun::Package> package)
			{
				ActivateActiveHyperlink(false);
				activeHyperlinks =
					!package ? nullptr :
					package->hyperlinks.Count() == 0 ? nullptr :
					package;
				ActivateActiveHyperlink(true);
				ActiveHyperlinkChanged.Execute(documentControl->GetNotifyEventArguments());
			}

			void GuiDocumentCommonInterface::ActivateActiveHyperlink(bool activate)
			{
				if (activeHyperlinks)
				{
					FOREACH(Ptr<DocumentHyperlinkRun>, run, activeHyperlinks->hyperlinks)
					{
						run->styleName = activate ? run->activeStyleName : run->normalStyleName;
					}
					documentElement->NotifyParagraphUpdated(activeHyperlinks->row, 1, 1, false);
				}
			}

			void GuiDocumentCommonInterface::AddShortcutCommand(vint key, const Func<void()>& eventHandler)
			{
				IGuiShortcutKeyItem* item=internalShortcutKeyManager->CreateShortcut(true, false, false, key);
				item->Executed.AttachLambda([=](GuiGraphicsComposition* sender, GuiEventArgs& arguments)
				{
					eventHandler();
				});
			}

			void GuiDocumentCommonInterface::EditTextInternal(TextPos begin, TextPos end, const Func<void(TextPos, TextPos, vint&, vint&)>& editor)
			{
				// save run before editing
				if(begin>end)
				{
					TextPos temp=begin;
					begin=end;
					end=temp;
				}
				Ptr<DocumentModel> originalModel=documentElement->GetDocument()->CopyDocument(begin, end, true);
				if(originalModel)
				{
					// edit
					vint paragraphCount=0;
					vint lastParagraphLength=0;
					editor(begin, end, paragraphCount, lastParagraphLength);

					// calculate new caret
					TextPos caret;
					if(paragraphCount==0)
					{
						caret=begin;
					}
					else if(paragraphCount==1)
					{
						caret=TextPos(begin.row, begin.column+lastParagraphLength);
					}
					else
					{
						caret=TextPos(begin.row+paragraphCount-1, lastParagraphLength);
					}
					documentElement->SetCaret(caret, caret, true);
					documentControl->TextChanged.Execute(documentControl->GetNotifyEventArguments());
					UpdateCaretPoint();
					SelectionChanged.Execute(documentControl->GetNotifyEventArguments());

					// save run after editing
					Ptr<DocumentModel> inputModel=documentElement->GetDocument()->CopyDocument(begin, caret, true);

					// submit redo-undo
					GuiDocumentUndoRedoProcessor::ReplaceModelStruct arguments;
					arguments.originalStart=begin;
					arguments.originalEnd=end;
					arguments.originalModel=originalModel;
					arguments.inputStart=begin;
					arguments.inputEnd=caret;
					arguments.inputModel=inputModel;
					undoRedoProcessor->OnReplaceModel(arguments);
				}
			}

			void GuiDocumentCommonInterface::EditStyleInternal(TextPos begin, TextPos end, const Func<void(TextPos, TextPos)>& editor)
			{
				// save run before editing
				if(begin>end)
				{
					TextPos temp=begin;
					begin=end;
					end=temp;
				}
				Ptr<DocumentModel> originalModel=documentElement->GetDocument()->CopyDocument(begin, end, true);
				if(originalModel)
				{
					// edit
					editor(begin, end);

					// save run after editing
					Ptr<DocumentModel> inputModel=documentElement->GetDocument()->CopyDocument(begin, end, true);

					// submit redo-undo
					GuiDocumentUndoRedoProcessor::ReplaceModelStruct arguments;
					arguments.originalStart=begin;
					arguments.originalEnd=end;
					arguments.originalModel=originalModel;
					arguments.inputStart=begin;
					arguments.inputEnd=end;
					arguments.inputModel=inputModel;
					undoRedoProcessor->OnReplaceModel(arguments);
				}
			}

			void GuiDocumentCommonInterface::MergeBaselineAndDefaultFont(Ptr<DocumentModel> document)
			{
				document->MergeDefaultFont(documentControl->GetFont());
				if (baselineDocument)
				{
					document->MergeBaselineStyles(baselineDocument);
				}
			}

			void GuiDocumentCommonInterface::OnFontChanged(compositions::GuiGraphicsComposition* sender, compositions::GuiEventArgs& arguments)
			{
				auto document = documentElement->GetDocument();
				MergeBaselineAndDefaultFont(document);
				documentElement->SetDocument(document);
			}

			void GuiDocumentCommonInterface::OnCaretNotify(compositions::GuiGraphicsComposition* sender, compositions::GuiEventArgs& arguments)
			{
				if(documentControl->GetVisuallyEnabled())
				{
					if(editMode!=ViewOnly)
					{
						documentElement->SetCaretVisible(!documentElement->GetCaretVisible());
					}
				}
			}

			void GuiDocumentCommonInterface::OnGotFocus(compositions::GuiGraphicsComposition* sender, compositions::GuiEventArgs& arguments)
			{
				if(documentControl->GetVisuallyEnabled())
				{
					if(editMode!=ViewOnly)
					{
						documentElement->SetCaretVisible(true);
						UpdateCaretPoint();
					}
				}
			}

			void GuiDocumentCommonInterface::OnLostFocus(compositions::GuiGraphicsComposition* sender, compositions::GuiEventArgs& arguments)
			{
				if(documentControl->GetVisuallyEnabled())
				{
					documentElement->SetCaretVisible(false);
				}
			}

			void GuiDocumentCommonInterface::OnKeyDown(compositions::GuiGraphicsComposition* sender, compositions::GuiKeyEventArgs& arguments)
			{
				if(documentControl->GetVisuallyEnabled())
				{
					if(editMode!=ViewOnly)
					{
						if(ProcessKey(arguments.code, arguments.shift, arguments.ctrl))
						{
							arguments.handled=true;
						}
					}
				}
			}

			void GuiDocumentCommonInterface::OnCharInput(compositions::GuiGraphicsComposition* sender, compositions::GuiCharEventArgs& arguments)
			{
				if(documentControl->GetVisuallyEnabled())
				{
					if(editMode==Editable && arguments.code!=VKEY_ESCAPE && arguments.code!=VKEY_BACK && arguments.code!=VKEY_RETURN && !arguments.ctrl)
					{
						Array<WString> text(1);
						text[0]=WString(arguments.code);
						EditText(documentElement->GetCaretBegin(), documentElement->GetCaretEnd(), documentElement->IsCaretEndPreferFrontSide(), text);
					}
				}
			}

			void GuiDocumentCommonInterface::OnMouseMove(compositions::GuiGraphicsComposition* sender, compositions::GuiMouseEventArgs& arguments)
			{
				if(documentControl->GetVisuallyEnabled())
				{
					switch(editMode)
					{
					case ViewOnly:
						{
							auto package = documentElement->GetHyperlinkFromPoint({ arguments.x, arguments.y });
							bool handCursor = false;

							if(dragging)
							{
								if(activeHyperlinks)
								{
									if (package && CompareEnumerable(activeHyperlinks->hyperlinks, package->hyperlinks) == 0)
									{
										ActivateActiveHyperlink(true);
										handCursor = true;
									}
									else
									{
										ActivateActiveHyperlink(false);
									}
								}
							}
							else
							{
								SetActiveHyperlink(package);
								handCursor = activeHyperlinks;
							}

							if(handCursor)
							{
								auto cursor = GetCurrentController()->ResourceService()->GetSystemCursor(INativeCursor::Hand);
								documentComposition->SetAssociatedCursor(cursor);
							}
							else
							{
								documentComposition->SetAssociatedCursor(nullptr);
							}
						}
						break;
					case Selectable:
					case Editable:
						if(dragging)
						{
							TextPos caret=documentElement->CalculateCaretFromPoint(Point(arguments.x, arguments.y));
							TextPos oldCaret=documentElement->GetCaretBegin();
							Move(caret, true, (oldCaret==caret?documentElement->IsCaretEndPreferFrontSide():caret<oldCaret));
						}
						break;
					}
				}
			}

			void GuiDocumentCommonInterface::OnMouseDown(compositions::GuiGraphicsComposition* sender, compositions::GuiMouseEventArgs& arguments)
			{
				if(documentControl->GetVisuallyEnabled())
				{
					documentControl->SetFocus();
					switch(editMode)
					{
					case ViewOnly:
						SetActiveHyperlink(documentElement->GetHyperlinkFromPoint({ arguments.x, arguments.y }));
						break;
					case Selectable:
					case Editable:
						{
							TextPos caret=documentElement->CalculateCaretFromPoint(Point(arguments.x, arguments.y));
							TextPos oldCaret=documentElement->GetCaretEnd();
							if(caret!=oldCaret)
							{
								Move(caret, arguments.shift, caret<oldCaret);
							}
						}
						break;
					}
					dragging=true;
				}
			}

			void GuiDocumentCommonInterface::OnMouseUp(compositions::GuiGraphicsComposition* sender, compositions::GuiMouseEventArgs& arguments)
			{
				if(documentControl->GetVisuallyEnabled())
				{
					dragging=false;
					switch(editMode)
					{
					case ViewOnly:
						{
							auto package = documentElement->GetHyperlinkFromPoint({ arguments.x, arguments.y });
							if(activeHyperlinks)
							{
								if (package && CompareEnumerable(activeHyperlinks->hyperlinks, package->hyperlinks) == 0)
								{
									ActiveHyperlinkExecuted.Execute(documentControl->GetNotifyEventArguments());
								}
								else
								{
									SetActiveHyperlink(nullptr);
								}
							}
						}
						break;
					default:;
					}
				}
			}

			void GuiDocumentCommonInterface::OnMouseLeave(compositions::GuiGraphicsComposition* sender, compositions::GuiEventArgs& arguments)
			{
				SetActiveHyperlink(0);
			}

			Point GuiDocumentCommonInterface::GetDocumentViewPosition()
			{
				return Point(0, 0);
			}

			void GuiDocumentCommonInterface::EnsureRectVisible(Rect bounds)
			{
			}

			//================ callback

			void GuiDocumentCommonInterface::OnStartRender()
			{
				FOREACH(Ptr<GuiDocumentItem>, item, documentItems.Values())
				{
					item->visible = false;
				}
			}

			void GuiDocumentCommonInterface::OnFinishRender()
			{
				FOREACH(Ptr<GuiDocumentItem>, item, documentItems.Values())
				{
					if (item->container->GetVisible() != item->visible)
					{
						item->container->SetVisible(item->visible);
					}
				}
			}

			Size GuiDocumentCommonInterface::OnRenderEmbeddedObject(const WString& name, const Rect& location)
			{
				vint index = documentItems.Keys().IndexOf(name);
				if (index != -1)
				{
					auto item = documentItems.Values()[index];
					auto size = item->container->GetBounds().GetSize();
					item->container->SetBounds(Rect(location.LeftTop(), Size(0, 0)));
					item->visible = true;
					return size;
				}
				return Size();
			}

			//================ basic

			GuiDocumentCommonInterface::GuiDocumentCommonInterface()
			{
				undoRedoProcessor=new GuiDocumentUndoRedoProcessor;

				internalShortcutKeyManager=new GuiShortcutKeyManager;
				AddShortcutCommand('Z', Func<bool()>(this, &GuiDocumentCommonInterface::Undo));
				AddShortcutCommand('Y', Func<bool()>(this, &GuiDocumentCommonInterface::Redo));
				AddShortcutCommand('A', Func<void()>(this, &GuiDocumentCommonInterface::SelectAll));
				AddShortcutCommand('X', Func<bool()>(this, &GuiDocumentCommonInterface::Cut));
				AddShortcutCommand('C', Func<bool()>(this, &GuiDocumentCommonInterface::Copy));
				AddShortcutCommand('V', Func<bool()>(this, &GuiDocumentCommonInterface::Paste));
			}

			GuiDocumentCommonInterface::~GuiDocumentCommonInterface()
			{
			}

			Ptr<DocumentModel> GuiDocumentCommonInterface::GetDocument()
			{
				return documentElement->GetDocument();
			}

			void GuiDocumentCommonInterface::SetDocument(Ptr<DocumentModel> value)
			{
				SetActiveHyperlink(0);
				ClearUndoRedo();
				NotifyModificationSaved();

				if (value)
				{
					if (value->paragraphs.Count() == 0)
					{
						value->paragraphs.Add(new DocumentParagraphRun);
					}
					MergeBaselineAndDefaultFont(value);
				}

				documentElement->SetDocument(value);
			}

			//================ document items

			bool GuiDocumentCommonInterface::AddDocumentItem(Ptr<GuiDocumentItem> value)
			{
				if (documentItems.Keys().Contains(value->GetName()))
				{
					return false;
				}
				documentItems.Add(value->GetName(), value);
				documentComposition->AddChild(value->container);
				value->visible = false;
				value->owned = true;
				value->container->SetVisible(false);
				return true;
			}

			bool GuiDocumentCommonInterface::RemoveDocumentItem(Ptr<GuiDocumentItem> value)
			{
				vint index = documentItems.Keys().IndexOf(value->GetName());
				if (index == -1)
				{
					return false;
				}
				if (documentItems.Values()[index] != value)
				{
					return false;
				}
				value->owned = false;
				documentComposition->RemoveChild(value->container);
				documentItems.Remove(value->GetName());
				return true;
			}

			const GuiDocumentCommonInterface::DocumentItemMap& GuiDocumentCommonInterface::GetDocumentItems()
			{
				return documentItems;
			}

			//================ caret operations

			TextPos GuiDocumentCommonInterface::GetCaretBegin()
			{
				return documentElement->GetCaretBegin();
			}

			TextPos GuiDocumentCommonInterface::GetCaretEnd()
			{
				return documentElement->GetCaretEnd();
			}

			void GuiDocumentCommonInterface::SetCaret(TextPos begin, TextPos end)
			{
				documentElement->SetCaret(begin, end, end>=begin);
				UpdateCaretPoint();
				SelectionChanged.Execute(documentControl->GetNotifyEventArguments());
			}

			TextPos GuiDocumentCommonInterface::CalculateCaretFromPoint(Point point)
			{
				return documentElement->CalculateCaretFromPoint(point);
			}

			Rect GuiDocumentCommonInterface::GetCaretBounds(TextPos caret, bool frontSide)
			{
				return documentElement->GetCaretBounds(caret, frontSide);
			}

			//================ editing operations

			void GuiDocumentCommonInterface::NotifyParagraphUpdated(vint index, vint oldCount, vint newCount, bool updatedText)
			{
				documentElement->NotifyParagraphUpdated(index, oldCount, newCount, updatedText);
			}

			void GuiDocumentCommonInterface::EditRun(TextPos begin, TextPos end, Ptr<DocumentModel> model, bool copy)
			{
				EditTextInternal(begin, end, [=](TextPos begin, TextPos end, vint& paragraphCount, vint& lastParagraphLength)
				{
					documentElement->EditRun(begin, end, model, copy);
					paragraphCount=model->paragraphs.Count();
					lastParagraphLength=paragraphCount==0?0:model->paragraphs[paragraphCount-1]->GetText(false).Length();
				});
			}

			void GuiDocumentCommonInterface::EditText(TextPos begin, TextPos end, bool frontSide, const collections::Array<WString>& text)
			{
				EditTextInternal(begin, end, [=, &text](TextPos begin, TextPos end, vint& paragraphCount, vint& lastParagraphLength)
				{
					documentElement->EditText(begin, end, frontSide, text);
					paragraphCount=text.Count();
					lastParagraphLength=paragraphCount==0?0:text[paragraphCount-1].Length();
				});
			}

			void GuiDocumentCommonInterface::EditStyle(TextPos begin, TextPos end, Ptr<DocumentStyleProperties> style)
			{
				EditStyleInternal(begin, end, [=](TextPos begin, TextPos end)
				{
					documentElement->EditStyle(begin, end, style);
				});
			}

			void GuiDocumentCommonInterface::EditImage(TextPos begin, TextPos end, Ptr<GuiImageData> image)
			{
				EditTextInternal(begin, end, [=](TextPos begin, TextPos end, vint& paragraphCount, vint& lastParagraphLength)
				{
					documentElement->EditImage(begin, end, image);
					paragraphCount=1;
					lastParagraphLength=wcslen(DocumentImageRun::RepresentationText);
				});
			}

			void GuiDocumentCommonInterface::EditHyperlink(vint paragraphIndex, vint begin, vint end, const WString& reference, const WString& normalStyleName, const WString& activeStyleName)
			{
				EditStyleInternal(TextPos(paragraphIndex, begin), TextPos(paragraphIndex, end), [=](TextPos begin, TextPos end)
				{
					documentElement->EditHyperlink(begin.row, begin.column, end.column, reference, normalStyleName, activeStyleName);
				});
			}

			void GuiDocumentCommonInterface::RemoveHyperlink(vint paragraphIndex, vint begin, vint end)
			{
				EditStyleInternal(TextPos(paragraphIndex, begin), TextPos(paragraphIndex, end), [=](TextPos begin, TextPos end)
				{
					documentElement->RemoveHyperlink(begin.row, begin.column, end.column);
				});
			}

			void GuiDocumentCommonInterface::EditStyleName(TextPos begin, TextPos end, const WString& styleName)
			{
				EditStyleInternal(begin, end, [=](TextPos begin, TextPos end)
				{
					documentElement->EditStyleName(begin, end, styleName);
				});
			}

			void GuiDocumentCommonInterface::RemoveStyleName(TextPos begin, TextPos end)
			{
				EditStyleInternal(begin, end, [=](TextPos begin, TextPos end)
				{
					documentElement->RemoveStyleName(begin, end);
				});
			}

			void GuiDocumentCommonInterface::RenameStyle(const WString& oldStyleName, const WString& newStyleName)
			{
				documentElement->RenameStyle(oldStyleName, newStyleName);

				// submit redo-undo
				GuiDocumentUndoRedoProcessor::RenameStyleStruct arguments;
				arguments.oldStyleName=oldStyleName;
				arguments.newStyleName=newStyleName;
				undoRedoProcessor->OnRenameStyle(arguments);
			}

			void GuiDocumentCommonInterface::ClearStyle(TextPos begin, TextPos end)
			{
				EditStyleInternal(begin, end, [=](TextPos begin, TextPos end)
				{
					documentElement->ClearStyle(begin, end);
				});
			}

			Ptr<DocumentStyleProperties> GuiDocumentCommonInterface::SummarizeStyle(TextPos begin, TextPos end)
			{
				if (begin>end)
				{
					TextPos temp = begin;
					begin = end;
					end = temp;
				}
				return documentElement->SummarizeStyle(begin, end);
			}

			Nullable<WString> GuiDocumentCommonInterface::SummarizeStyleName(TextPos begin, TextPos end)
			{
				if (begin>end)
				{
					TextPos temp = begin;
					begin = end;
					end = temp;
				}
				return documentElement->SummarizeStyleName(begin, end);
			}

			void GuiDocumentCommonInterface::SetParagraphAlignments(TextPos begin, TextPos end, const collections::Array<Nullable<Alignment>>& alignments)
			{
				vint first = begin.row;
				vint last = end.row;
				if (first > last)
				{
					vint temp = first;
					first = last;
					last = temp;
				}

				Ptr<DocumentModel> document = documentElement->GetDocument();
				if (0 <= first && first < document->paragraphs.Count() && 0 <= last && last < document->paragraphs.Count() && last - first + 1 == alignments.Count())
				{
					Ptr<GuiDocumentUndoRedoProcessor::SetAlignmentStruct> arguments = new GuiDocumentUndoRedoProcessor::SetAlignmentStruct;
					arguments->start = first;
					arguments->end = last;
					arguments->originalAlignments.Resize(alignments.Count());
					arguments->inputAlignments.Resize(alignments.Count());
					for (vint i = first; i <= last; i++)
					{
						arguments->originalAlignments[i - first] = document->paragraphs[i]->alignment;
						arguments->inputAlignments[i - first] = alignments[i - first];
					}
					documentElement->SetParagraphAlignment(begin, end, alignments);
					undoRedoProcessor->OnSetAlignment(arguments);
				}
			}

			void GuiDocumentCommonInterface::SetParagraphAlignment(TextPos begin, TextPos end, Nullable<Alignment> alignment)
			{
#if defined VCZH_GCC && defined VCZH_64
#define abs labs
#endif
				Array<Nullable<Alignment>> alignments(abs(begin.row - end.row) + 1);
#if defined VCZH_GCC && defined VCZH_64
#undef abs
#endif
				for (vint i = 0; i < alignments.Count(); i++)
				{
					alignments[i] = alignment;
				}
				SetParagraphAlignments(begin, end, alignments);
			}

			Nullable<Alignment> GuiDocumentCommonInterface::SummarizeParagraphAlignment(TextPos begin, TextPos end)
			{
				if (begin>end)
				{
					TextPos temp = begin;
					begin = end;
					end = temp;
				}
				return documentElement->SummarizeParagraphAlignment(begin, end);
			}

			//================ editing control

			WString GuiDocumentCommonInterface::GetActiveHyperlinkReference()
			{
				return activeHyperlinks ? activeHyperlinks->hyperlinks[0]->reference : L"";
			}

			GuiDocumentCommonInterface::EditMode GuiDocumentCommonInterface::GetEditMode()
			{
				return editMode;
			}

			void GuiDocumentCommonInterface::SetEditMode(EditMode value)
			{
				if(activeHyperlinks)
				{
					SetActiveHyperlink(nullptr);
				}

				editMode=value;
				if(editMode==ViewOnly)
				{
					documentComposition->SetAssociatedCursor(0);
				}
				else
				{
					INativeCursor* cursor=GetCurrentController()->ResourceService()->GetSystemCursor(INativeCursor::IBeam);
					documentComposition->SetAssociatedCursor(cursor);
				}
			}

			//================ selection operations

			void GuiDocumentCommonInterface::SelectAll()
			{
				vint lastIndex=documentElement->GetDocument()->paragraphs.Count()-1;
				Ptr<DocumentParagraphRun> lastParagraph=documentElement->GetDocument()->paragraphs[lastIndex];

				TextPos begin(0, 0);
				TextPos end(lastIndex, lastParagraph->GetText(false).Length());
				SetCaret(begin, end);
			}

			WString GuiDocumentCommonInterface::GetSelectionText()
			{
				TextPos begin=documentElement->GetCaretBegin();
				TextPos end=documentElement->GetCaretEnd();
				if(begin>end)
				{
					TextPos temp=begin;
					begin=end;
					end=temp;
				}

				Ptr<DocumentModel> model=documentElement->GetDocument()->CopyDocument(begin, end, false);
				return model->GetText(true);
			}

			void GuiDocumentCommonInterface::SetSelectionText(const WString& value)
			{
				List<WString> paragraphs;
				{
					stream::StringReader reader(value);
					WString paragraph;
					bool empty=true;

					while(!reader.IsEnd())
					{
						WString line=reader.ReadLine();
						if(empty)
						{
							paragraph+=line;
							empty=false;
						}
						else if(line!=L"")
						{
							paragraph+=L"\r\n"+line;
						}
						else
						{
							paragraphs.Add(paragraph);
							paragraph=L"";
							empty=true;
						}
					}

					if(!empty)
					{
						paragraphs.Add(paragraph);
					}
				}

				TextPos begin=documentElement->GetCaretBegin();
				TextPos end=documentElement->GetCaretEnd();
				if(begin>end)
				{
					TextPos temp=begin;
					begin=end;
					end=temp;
				}

				Array<WString> text;
				CopyFrom(text, paragraphs);
				EditText(begin, end, documentElement->IsCaretEndPreferFrontSide(), text);
			}

			Ptr<DocumentModel> GuiDocumentCommonInterface::GetSelectionModel()
			{
				TextPos begin=documentElement->GetCaretBegin();
				TextPos end=documentElement->GetCaretEnd();
				if(begin>end)
				{
					TextPos temp=begin;
					begin=end;
					end=temp;
				}

				Ptr<DocumentModel> model=documentElement->GetDocument()->CopyDocument(begin, end, true);
				return model;
			}

			void GuiDocumentCommonInterface::SetSelectionModel(Ptr<DocumentModel> value)
			{
				TextPos begin=documentElement->GetCaretBegin();
				TextPos end=documentElement->GetCaretEnd();
				if(begin>end)
				{
					TextPos temp=begin;
					begin=end;
					end=temp;
				}

				EditRun(begin, end, value, true);
			}

			//================ clipboard operations

			bool GuiDocumentCommonInterface::CanCut()
			{
				return editMode==Editable && documentElement->GetCaretBegin()!=documentElement->GetCaretEnd();
			}

			bool GuiDocumentCommonInterface::CanCopy()
			{
				return documentElement->GetCaretBegin()!=documentElement->GetCaretEnd();
			}

			bool GuiDocumentCommonInterface::CanPaste()
			{
				return editMode==Editable && GetCurrentController()->ClipboardService()->ContainsText();
			}

			bool GuiDocumentCommonInterface::Cut()
			{
				if(CanCut())
				{
					GetCurrentController()->ClipboardService()->SetText(GetSelectionText());
					SetSelectionText(L"");
					return true;
				}
				else
				{
					return false;
				}
			}

			bool GuiDocumentCommonInterface::Copy()
			{
				if(CanCopy())
				{
					GetCurrentController()->ClipboardService()->SetText(GetSelectionText());
					return true;
				}
				else
				{
					return false;
				}
			}

			bool GuiDocumentCommonInterface::Paste()
			{
				if(CanPaste())
				{
					SetSelectionText(GetCurrentController()->ClipboardService()->GetText());
					return true;
				}
				else
				{
					return false;
				}
			}

			//================ undo redo control

			bool GuiDocumentCommonInterface::CanUndo()
			{
				return editMode==Editable && undoRedoProcessor->CanUndo();
			}

			bool GuiDocumentCommonInterface::CanRedo()
			{
				return editMode==Editable && undoRedoProcessor->CanRedo();
			}

			void GuiDocumentCommonInterface::ClearUndoRedo()
			{
				undoRedoProcessor->ClearUndoRedo();
			}

			bool GuiDocumentCommonInterface::GetModified()
			{
				return undoRedoProcessor->GetModified();
			}

			void GuiDocumentCommonInterface::NotifyModificationSaved()
			{
				undoRedoProcessor->NotifyModificationSaved();
			}

			bool GuiDocumentCommonInterface::Undo()
			{
				if(CanUndo())
				{
					return undoRedoProcessor->Undo();
				}
				else
				{
					return false;
				}
			}

			bool GuiDocumentCommonInterface::Redo()
			{
				if(CanRedo())
				{
					return undoRedoProcessor->Redo();
				}
				else
				{
					return false;
				}
			}

/***********************************************************************
GuiDocumentViewer
***********************************************************************/

			void GuiDocumentViewer::BeforeControlTemplateUninstalled_()
			{
			}

			void GuiDocumentViewer::AfterControlTemplateInstalled_(bool initialize)
			{
				auto ct = GetControlTemplateObject(true);
				baselineDocument = ct->GetBaselineDocument();
				if (documentElement)
				{
					documentElement->SetCaretColor(ct->GetCaretColor());
					SetDocument(GetDocument());
				}
			}

			Point GuiDocumentViewer::GetDocumentViewPosition()
			{
				return GetViewBounds().LeftTop();
			}

			void GuiDocumentViewer::EnsureRectVisible(Rect bounds)
			{
				Rect viewBounds=GetViewBounds();
				vint offset=0;
				if(bounds.y1<viewBounds.y1)
				{
					offset=bounds.y1-viewBounds.y1;
				}
				else if(bounds.y2>viewBounds.y2)
				{
					offset=bounds.y2-viewBounds.y2;
				}

				if (auto scroll = GetVerticalScroll())
				{
					scroll->SetPosition(viewBounds.y1 + offset);
				}
			}

			GuiDocumentViewer::GuiDocumentViewer(theme::ThemeName themeName)
				:GuiScrollContainer(themeName)
			{
				SetFocusableComposition(boundsComposition);
				InstallDocumentViewer(this, containerComposition, boundsComposition, focusableComposition);

				SetExtendToFullWidth(true);
				SetHorizontalAlwaysVisible(false);
			}

			GuiDocumentViewer::~GuiDocumentViewer()
			{
			}

			const WString& GuiDocumentViewer::GetText()
			{
				text=documentElement->GetDocument()->GetText(true);
				return text;
			}

			void GuiDocumentViewer::SetText(const WString& value)
			{
				SelectAll();
				SetSelectionText(value);
			}

/***********************************************************************
GuiDocumentLabel
***********************************************************************/

			void GuiDocumentLabel::BeforeControlTemplateUninstalled_()
			{
			}

			void GuiDocumentLabel::AfterControlTemplateInstalled_(bool initialize)
			{
				auto ct = GetControlTemplateObject(true);
				baselineDocument = ct->GetBaselineDocument();
				if (documentElement)
				{
					documentElement->SetCaretColor(ct->GetCaretColor());
					SetDocument(GetDocument());
				}
			}

			GuiDocumentLabel::GuiDocumentLabel(theme::ThemeName themeName)
				:GuiControl(themeName)
			{
				SetFocusableComposition(boundsComposition);
				InstallDocumentViewer(this, containerComposition, boundsComposition, focusableComposition);
			}

			GuiDocumentLabel::~GuiDocumentLabel()
			{
			}

			const WString& GuiDocumentLabel::GetText()
			{
				text=documentElement->GetDocument()->GetText(true);
				return text;
			}

			void GuiDocumentLabel::SetText(const WString& value)
			{
				SelectAll();
				SetSelectionText(value);
			}
		}
	}
}


/***********************************************************************
.\CONTROLS\TEXTEDITORPACKAGE\LANGUAGESERVICE\GUILANGUAGEAUTOCOMPLETE.CPP
***********************************************************************/

namespace vl
{
	namespace presentation
	{
		namespace controls
		{
			using namespace elements;
			using namespace regex;
			using namespace parsing;
			using namespace parsing::tabling;
			using namespace collections;

/***********************************************************************
GuiGrammarAutoComplete
***********************************************************************/

			void GuiGrammarAutoComplete::Attach(elements::GuiColorizedTextElement* _element, SpinLock& _elementModifyLock, compositions::GuiGraphicsComposition* _ownerComposition, vuint editVersion)
			{
				GuiTextBoxAutoCompleteBase::Attach(_element, _elementModifyLock, _ownerComposition, editVersion);
				RepeatingParsingExecutor::CallbackBase::Attach(_element, _elementModifyLock, _ownerComposition, editVersion);
			}

			void GuiGrammarAutoComplete::Detach()
			{
				GuiTextBoxAutoCompleteBase::Detach();
				RepeatingParsingExecutor::CallbackBase::Detach();
				if(element && elementModifyLock)
				{
					EnsureAutoCompleteFinished();
				}
			}

			void GuiGrammarAutoComplete::TextEditPreview(TextEditPreviewStruct& arguments)
			{
				GuiTextBoxAutoCompleteBase::TextEditPreview(arguments);
				RepeatingParsingExecutor::CallbackBase::TextEditPreview(arguments);

				if(element && elementModifyLock)
				{
					if(IsListOpening() && arguments.keyInput && arguments.originalText==L"" && arguments.inputText!=L"")
					{
						WString selectedItem=GetSelectedListItem();
						if(selectedItem!=L"")
						{
							TextPos begin=GetListStartPosition();
							TextPos end=arguments.originalStart;
							WString editingText=element->GetLines().GetText(begin, end);
							editingText+=arguments.inputText;
							if(grammarParser->GetTable()->GetLexer().Walk().IsClosedToken(editingText))
							{
								arguments.originalStart=begin;
								arguments.inputText=selectedItem+arguments.inputText;
								CloseList();
							}
						}
					}
				}
			}

			void GuiGrammarAutoComplete::TextEditNotify(const TextEditNotifyStruct& arguments)
			{
				GuiTextBoxAutoCompleteBase::TextEditNotify(arguments);
				RepeatingParsingExecutor::CallbackBase::TextEditNotify(arguments);
				if(element && elementModifyLock)
				{
					editing=true;
					SPIN_LOCK(editTraceLock)
					{
						editTrace.Add(arguments);
					}
				}
			}

			void GuiGrammarAutoComplete::TextCaretChanged(const TextCaretChangedStruct& arguments)
			{
				GuiTextBoxAutoCompleteBase::TextCaretChanged(arguments);
				RepeatingParsingExecutor::CallbackBase::TextCaretChanged(arguments);
				if(element && elementModifyLock)
				{
					SPIN_LOCK(editTraceLock)
					{
						// queue a fake TextEditNotifyStruct
						// a fake struct can be detected by (trace.originalText==L"" && trace.inputText==L"")
						TextEditNotifyStruct trace;
						trace.editVersion=arguments.editVersion;
						trace.originalStart=arguments.oldBegin;
						trace.originalEnd=arguments.oldEnd;
						trace.inputStart=arguments.newBegin;
						trace.inputEnd=arguments.newEnd;

						// ensure trace.originalStart<=trace.originalEnd
						if(trace.originalStart>trace.originalEnd)
						{
							TextPos temp=trace.originalStart;
							trace.originalStart=trace.originalEnd;
							trace.originalEnd=temp;
						}
						// ensure trace.inputStart<=trace.inputEnd
						if(trace.inputStart>trace.inputEnd)
						{
							TextPos temp=trace.inputStart;
							trace.inputStart=trace.inputEnd;
							trace.inputEnd=temp;
						}
						editTrace.Add(trace);
					}

					SPIN_LOCK(contextLock)
					{
						if(context.input.node)
						{
							if(editing)
							{
								// if the current caret changing is caused by editing
								// submit a task with valid editVersion and invalid node and code
								RepeatingParsingOutput input;
								input.editVersion=context.input.editVersion;
								SubmitTask(input);
							}
							else if(context.input.editVersion == arguments.editVersion)
							{
								// if the current caret changing is not caused by editing
								// submit a task with the previous input
								SubmitTask(context.input);
							}
						}
					}
				}
			}

			void GuiGrammarAutoComplete::TextEditFinished(vuint editVersion)
			{
				GuiTextBoxAutoCompleteBase::TextEditFinished(editVersion);
				RepeatingParsingExecutor::CallbackBase::TextEditFinished(editVersion);
				if(element && elementModifyLock)
				{
					editing=false;
				}
			}

			void GuiGrammarAutoComplete::OnParsingFinishedAsync(const RepeatingParsingOutput& arguments)
			{
				if(element && elementModifyLock)
				{
					GetApplication()->InvokeInMainThread(ownerComposition->GetRelatedControlHost(), [=]()
					{
						// submit a task if the RepeatingParsingExecutor notices a new parsing result
						SubmitTask(arguments);
					});
				}
			}

			void GuiGrammarAutoComplete::CollectLeftRecursiveRules()
			{
				leftRecursiveRules.Clear();
				Ptr<ParsingGeneralParser> parser=parsingExecutor->GetParser();
				Ptr<ParsingTable> table=parser->GetTable();
				vint stateCount=table->GetStateCount();
				vint tokenCount=table->GetTokenCount();
				for(vint i=0;i<stateCount;i++)
				{
					for(vint j=0;j<tokenCount;j++)
					{
						Ptr<ParsingTable::TransitionBag> bag=table->GetTransitionBag(i, j);
						if(bag)
						{
							FOREACH(Ptr<ParsingTable::TransitionItem>, item, bag->transitionItems)
							{
								FOREACH(ParsingTable::Instruction, ins, item->instructions)
								{
									if(ins.instructionType==ParsingTable::Instruction::LeftRecursiveReduce)
									{
										if(!leftRecursiveRules.Contains(ins.creatorRule))
										{
											leftRecursiveRules.Add(ins.creatorRule);
										}
									}
								}
							}
						}
					}
				}
			}

			vint GuiGrammarAutoComplete::UnsafeGetEditTraceIndex(vuint editVersion)
			{
				// get the index of the latest TextEditNotifyStruct of a specified edit version
				// this function should be called inside SPIN_LOCK(editTraceLock)
				// perform a binary search
				vint start = 0;
				vint end = editTrace.Count() - 1;
				while (start <= end)
				{
					vint middle = (start + end) / 2;
					TextEditNotifyStruct& trace = editTrace[middle];

					if (editVersion<trace.editVersion)
					{
						end = middle - 1;
					}
					else if (editVersion>trace.editVersion)
					{
						start = middle + 1;
					}
					else
					{
						// if multiple TextEditNotifyStruct is found, choose the latest one
						while (middle < editTrace.Count() - 1)
						{
							if (editTrace[middle + 1].editVersion == editTrace[middle].editVersion)
							{
								middle++;
							}
							else
							{
								break;
							}
						}
						return middle;
					}
				}
				return -1;
			}

			TextPos GuiGrammarAutoComplete::ChooseCorrectTextPos(TextPos pos, const regex::RegexTokens& tokens)
			{
				Ptr<ParsingTable> table=grammarParser->GetTable();
				RegexToken lastToken;
				lastToken.reading=0;

				FOREACH(RegexToken, token, tokens)
				{
					// we treat "class| Name" as editing the first token
					if(TextPos(token.rowEnd, token.columnEnd+1)>=pos)
					{
						if(table->GetTableTokenIndex(token.token)!=-1 && lastToken.reading)
						{
							pos=TextPos(lastToken.rowStart, lastToken.columnStart);
						}
						break;
					}
					lastToken=token;
				}
				return pos;
			}

			void GuiGrammarAutoComplete::ExecuteRefresh(AutoCompleteContext& newContext)
			{
				// process the input of a task is submitted not by text editing
				// find the text selection by the edit version of the input
				TextPos startPos, endPos;
				{
					SPIN_LOCK(editTraceLock)
					{
						vint traceIndex = UnsafeGetEditTraceIndex(newContext.input.editVersion);
						if (traceIndex == -1) return;

						TextEditNotifyStruct& trace = editTrace[traceIndex];
						startPos = trace.inputStart;
						endPos = trace.inputEnd;
					}

					const RegexLexer& lexer = grammarParser->GetTable()->GetLexer();
					RegexTokens tokens = lexer.Parse(newContext.input.code);
					startPos = ChooseCorrectTextPos(startPos, tokens);
				}

				// locate the deepest node using the text selection
				ParsingTextPos start(startPos.row, startPos.column);
				ParsingTextPos end(endPos.row, endPos.column);
				ParsingTextRange range(start, end);
				ParsingTreeNode* found = newContext.input.node->FindDeepestNode(range);
				ParsingTreeObject* selectedNode = 0;

				// if the location failed, choose the root node
				if (!found || startPos == TextPos(0, 0))
				{
					found = newContext.input.node.Obj();
				}

				if (!selectedNode)
				{
					// from the deepest node, traverse towards the root node
					// find the deepest node whose created rule is a left recursive rule and whose parent is not
					ParsingTreeObject* lrec = 0;
					ParsingTreeNode* current = found;
					while (current)
					{
						ParsingTreeObject* obj = dynamic_cast<ParsingTreeObject*>(current);
						if (obj)
						{
							FOREACH(WString, rule, obj->GetCreatorRules())
							{
								if (leftRecursiveRules.Contains(rule))
								{
									lrec = obj;
									break;
								}
							}
							if (obj && lrec && lrec != obj)
							{
								selectedNode = lrec;
								break;
							}
						}
						current = current->GetParent();
					}
				}

				if (!selectedNode)
				{
					// if there is no left recursive rule that creates the deepest node and all indirect parents
					// choose the deepest ParsingTreeObject
					ParsingTreeNode* current = found;
					while (current)
					{
						ParsingTreeObject* obj = dynamic_cast<ParsingTreeObject*>(current);
						if (obj)
						{
							selectedNode = obj;
							break;
						}
						current = current->GetParent();
					}
				}

				if (selectedNode)
				{
					// get the code range of the selected node
					start = selectedNode->GetCodeRange().start;
					end = selectedNode->GetCodeRange().end;

					// get all properties from the selected node
					newContext.rule = selectedNode->GetCreatorRules()[selectedNode->GetCreatorRules().Count() - 1];
					newContext.originalRange = selectedNode->GetCodeRange();
					newContext.originalNode = dynamic_cast<ParsingTreeObject*>(selectedNode);
					newContext.modifiedNode = newContext.originalNode;
					newContext.modifiedEditVersion = newContext.input.editVersion;

					// get the corresponding code of the selected node
					if (start.index >= 0 && end.index >= 0)
					{
						newContext.modifiedCode = newContext.input.code.Sub(start.index, end.index - start.index + 1).Buffer();
					}
				}
			}

			bool GuiGrammarAutoComplete::NormalizeTextPos(AutoCompleteContext& newContext, elements::text::TextLines& lines, TextPos& pos)
			{
				// get the start position
				TextPos start(newContext.originalRange.start.row, newContext.originalRange.start.column);

				// get the end position of the end of lines
				TextPos end
					= lines.GetCount() <= 1
					? TextPos(start.row, start.column + lines.GetLine(0).dataLength)
					: TextPos(start.row + lines.GetCount() - 1, lines.GetLine(lines.GetCount() - 1).dataLength)
					;

				if (start <= pos && pos <= end)
				{
					// if the pos is inside the range
					// normalize the pos to a new coordinate that the beginning position of lines is (row=0, column=0)
					pos.row -= start.row;
					if (pos.row == 0)
					{
						pos.column -= start.column;
					}
					return true;
				}
				else
				{
					return false;
				}
			}

			void GuiGrammarAutoComplete::ExecuteEdit(AutoCompleteContext& newContext)
			{
				// process the input of a task that is submitted by text editing
				// this function make an approximiation to the context if the RepeatingParsingExecutor is not fast enough
				// copy all TextEditNotifyStruct that is caused by a text editing before (and including) the edit version of the input
				List<TextEditNotifyStruct> usedTrace;
				{
					SPIN_LOCK(editTraceLock)
					{
						CopyFrom(
							usedTrace,
							From(editTrace)
							.Where([&newContext](const TextEditNotifyStruct& value)
								{
									return (value.originalText != L"" || value.inputText != L"") && value.editVersion > newContext.modifiedEditVersion;
								})
						);
					}
				}

				// apply all modification to get the new modifiedCode
				bool failed = false;
				if (usedTrace.Count() > 0)
				{
					if (usedTrace[0].editVersion != newContext.modifiedEditVersion + 1)
					{
						// failed if any TextEditNotifyStruct is missing 
						failed = true;
					}
					else
					{
						// initialize a TextLines with the latest modifiedCode
						text::TextLines lines(nullptr);
						lines.SetText(newContext.modifiedCode);
						FOREACH(TextEditNotifyStruct, trace, usedTrace)
						{
							// apply a modification to lines
							TextPos start = trace.originalStart;
							TextPos end = trace.originalEnd;

							// only if the modification is meaningful
							if (NormalizeTextPos(newContext, lines, start) && NormalizeTextPos(newContext, lines, end))
							{
								lines.Modify(start, end, trace.inputText);
							}
							else
							{
								// otherwise, failed
								failed = true;
								break;
							}
						}

						if (!failed)
						{
							newContext.modifiedCode = lines.GetText();
						}
					}
				}

				if (failed)
				{
					// clear originalNode to notify that the current context goes wrong
					newContext.originalNode = 0;
				}

				if (usedTrace.Count() > 0)
				{
					// update the edit version
					newContext.modifiedEditVersion = usedTrace[usedTrace.Count() - 1].editVersion;
				}
			}

			void GuiGrammarAutoComplete::DeleteFutures(collections::List<parsing::tabling::ParsingState::Future*>& futures)
			{
				// delete all futures and clear the list
				FOREACH(ParsingState::Future*, future, futures)
				{
					delete future;
				}
				futures.Clear();
			}

			regex::RegexToken* GuiGrammarAutoComplete::TraverseTransitions(
				parsing::tabling::ParsingState& state,
				parsing::tabling::ParsingTransitionCollector& transitionCollector,
				TextPos stopPosition,
				collections::List<parsing::tabling::ParsingState::Future*>& nonRecoveryFutures,
				collections::List<parsing::tabling::ParsingState::Future*>& recoveryFutures
				)
			{
				const List<ParsingState::TransitionResult>& transitions = transitionCollector.GetTransitions();
				for (vint index = 0; index < transitions.Count(); index++)
				{
					const ParsingState::TransitionResult& transition = transitions[index];
					switch (transition.transitionType)
					{
					case ParsingState::TransitionResult::AmbiguityBegin:
						break;
					case ParsingState::TransitionResult::AmbiguityBranch:
						// ambiguity branches are not nested
						// tokens in different braches are the same
						// so we only need to run one branch, and skip the others
						index = transitionCollector.GetAmbiguityEndFromBegin(transitionCollector.GetAmbiguityBeginFromBranch(index));
						break;
					case ParsingState::TransitionResult::AmbiguityEnd:
						break;
					case ParsingState::TransitionResult::ExecuteInstructions:
						{
							// test does the token reach the stop position
							if (transition.token)
							{
								// we treat "A|B" as editing A if token A is endless, otherwise treated as editing B
								TextPos tokenEnd(transition.token->rowEnd, transition.token->columnEnd + 1);

								// if the caret is not at the end of the token
								if (tokenEnd > stopPosition)
								{
									// stop the traversing and return the editing token
									return transition.token;
								}
								else if (tokenEnd == stopPosition)
								{
									// if the caret is at the end of the token, and it is a closed token
									// e.g. identifier is not a closed token, string is a closed token
									if (!grammarParser->GetTable()->GetLexer().Walk().IsClosedToken(transition.token->reading, transition.token->length))
									{
										// stop the traversing and return the editing token
										return transition.token;
									}
								}
							}

							// traverse the PDA using the token specified in the current transition
							vint tableTokenIndex = transition.tableTokenIndex;
							List<ParsingState::Future*> possibilities;
							if (recoveryFutures.Count() > 0)
							{
								FOREACH(ParsingState::Future*, future, recoveryFutures)
								{
									state.Explore(tableTokenIndex, future, possibilities);
								}
							}
							else
							{
								FOREACH(ParsingState::Future*, future, nonRecoveryFutures)
								{
									state.Explore(tableTokenIndex, future, possibilities);
								}
							}

							// delete duplicated futures
							List<ParsingState::Future*> selectedPossibilities;
							for (vint i = 0; i < possibilities.Count(); i++)
							{
								ParsingState::Future* candidateFuture = possibilities[i];
								bool duplicated = false;
								FOREACH(ParsingState::Future*, future, selectedPossibilities)
								{
									if (
										candidateFuture->currentState == future->currentState &&
										candidateFuture->reduceStateCount == future->reduceStateCount &&
										candidateFuture->shiftStates.Count() == future->shiftStates.Count()
										)
									{
										bool same = true;
										for (vint j = 0; j < future->shiftStates.Count(); j++)
										{
											if (candidateFuture->shiftStates[i] != future->shiftStates[i])
											{
												same = false;
												break;
											}
										}

										if ((duplicated = same))
										{
											break;
										}
									}
								}

								if (duplicated)
								{
									delete candidateFuture;
								}
								else
								{
									selectedPossibilities.Add(candidateFuture);
								}
							}

							// step forward
							if (transition.token || transition.tableTokenIndex == ParsingTable::TokenBegin)
							{
								DeleteFutures(nonRecoveryFutures);
								DeleteFutures(recoveryFutures);
								CopyFrom(nonRecoveryFutures, selectedPossibilities);
							}
							else
							{
								DeleteFutures(recoveryFutures);
								CopyFrom(recoveryFutures, selectedPossibilities);
							}
						}
						break;
					default:;
					}
				}
				return 0;
			}

			regex::RegexToken* GuiGrammarAutoComplete::SearchValidInputToken(
				parsing::tabling::ParsingState& state,
				parsing::tabling::ParsingTransitionCollector& transitionCollector,
				TextPos stopPosition,
				AutoCompleteContext& newContext,
				collections::SortedList<vint>& tableTokenIndices
				)
			{
				// initialize the PDA state
				state.Reset(newContext.rule);
				List<ParsingState::Future*> nonRecoveryFutures, recoveryFutures;
				nonRecoveryFutures.Add(state.ExploreCreateRootFuture());

				// traverse the PDA until it reach the stop position
				// nonRecoveryFutures store the state when the last token (existing) is reached
				// recoveryFutures store the state when the last token (inserted by error recovery) is reached
				RegexToken* token = TraverseTransitions(state, transitionCollector, stopPosition, nonRecoveryFutures, recoveryFutures);

				// explore all possibilities from the last token before the stop position
				List<ParsingState::Future*> possibilities;
				for (vint i = 0; i < nonRecoveryFutures.Count(); i++)
				{
					state.Explore(ParsingTable::NormalReduce, nonRecoveryFutures[i], nonRecoveryFutures);
					state.Explore(ParsingTable::LeftRecursiveReduce, nonRecoveryFutures[i], nonRecoveryFutures);
				}
				FOREACH(ParsingState::Future*, future, nonRecoveryFutures)
				{
					vint count = state.GetTable()->GetTokenCount();
					for (vint i = ParsingTable::UserTokenStart; i < count; i++)
					{
						state.Explore(i, future, possibilities);
					}
				}

				// get all possible tokens that marked using @AutoCompleteCandidate
				FOREACH(ParsingState::Future*, future, possibilities)
				{
					if (!tableTokenIndices.Contains(future->selectedToken))
					{
						tableTokenIndices.Add(future->selectedToken);
					}
				}

				// release all data
				DeleteFutures(possibilities);
				DeleteFutures(nonRecoveryFutures);
				DeleteFutures(recoveryFutures);

				// return the editing token
				return token;
			}

			TextPos GuiGrammarAutoComplete::GlobalTextPosToModifiedTextPos(AutoCompleteContext& newContext, TextPos pos)
			{
				pos.row-=newContext.originalRange.start.row;
				if(pos.row==0)
				{
					pos.column-=newContext.originalRange.start.column;
				}
				return pos;
			}

			TextPos GuiGrammarAutoComplete::ModifiedTextPosToGlobalTextPos(AutoCompleteContext& newContext, TextPos pos)
			{
				if(pos.row==0)
				{
					pos.column+=newContext.originalRange.start.column;
				}
				pos.row+=newContext.originalRange.start.row;
				return pos;
			}

			void GuiGrammarAutoComplete::ExecuteCalculateList(AutoCompleteContext& newContext)
			{
				// calcuate the content of the auto complete list
				// it is sad that, because the parser's algorithm is too complex
				// we need to reparse and track the internal state of the PDA(push-down automaton) here.
				// initialize the PDA
				ParsingState state(newContext.modifiedCode, grammarParser->GetTable());
				state.Reset(newContext.rule);

				// prepare to get all transitions
				ParsingTransitionCollector collector;
				List<Ptr<ParsingError>> errors;

				// reparse and get all transitions during parsing
				if (grammarParser->Parse(state, collector, errors))
				{
					// if modifiedNode is not prepared (the task is submitted because of text editing)
					// use the transition to build the syntax tree
					if (!newContext.modifiedNode)
					{
						ParsingTreeBuilder builder;
						builder.Reset();
						bool succeeded = true;
						FOREACH(ParsingState::TransitionResult, transition, collector.GetTransitions())
						{
							if (!(succeeded = builder.Run(transition)))
							{
								break;
							}
						}

						if (succeeded)
						{
							Ptr<ParsingTreeNode> parsedNode = builder.GetNode();
							newContext.modifiedNode = parsedNode.Cast<ParsingTreeObject>();
							newContext.modifiedNode->InitializeQueryCache();
						}
					}

					if (newContext.modifiedNode)
					{
						// get the latest text editing trace
						TextEditNotifyStruct trace;
						SPIN_LOCK(editTraceLock)
						{
							vint index = UnsafeGetEditTraceIndex(newContext.modifiedEditVersion);
							if (index == -1)
							{
								return;
							}
							else
							{
								trace = editTrace[index];
							}
						}

						// calculate the stop position for PDA traversing
						TextPos stopPosition = GlobalTextPosToModifiedTextPos(newContext, trace.inputStart);

						// find all possible token before the current caret using the PDA
						Ptr<AutoCompleteData> autoComplete = new AutoCompleteData;
						SortedList<vint> tableTokenIndices;
						RegexToken* editingToken = SearchValidInputToken(state, collector, stopPosition, newContext, tableTokenIndices);

						// collect all auto complete types
						{
							// collect all keywords that can be put into the auto complete list
							FOREACH(vint, token, tableTokenIndices)
							{
								vint regexToken = token - ParsingTable::UserTokenStart;
								if (regexToken >= 0)
								{
									autoComplete->candidates.Add(regexToken);
									if (parsingExecutor->GetTokenMetaData(regexToken).isCandidate)
									{
										autoComplete->shownCandidates.Add(regexToken);
									}
								}
							}

							// calculate the arranged stopPosition
							if (editingToken)
							{
								TextPos tokenPos(editingToken->rowStart, editingToken->columnStart);
								if (tokenPos < stopPosition)
								{
									stopPosition = tokenPos;
								}
							}

							// calculate the start/end position for PDA traversing
							TextPos startPos, endPos;
							{
								startPos = ModifiedTextPosToGlobalTextPos(newContext, stopPosition);
								autoComplete->startPosition = startPos;
								endPos = trace.inputEnd;
								if (newContext.modifiedNode != newContext.originalNode)
								{
									startPos = GlobalTextPosToModifiedTextPos(newContext, startPos);
									endPos = GlobalTextPosToModifiedTextPos(newContext, endPos);
								}
								if (startPos<endPos && endPos.column>0)
								{
									endPos.column--;
								}
							}

							// calculate the auto complete type
							if (editingToken && parsingExecutor->GetTokenMetaData(editingToken->token).hasAutoComplete)
							{
								ParsingTextRange range(ParsingTextPos(startPos.row, startPos.column), ParsingTextPos(endPos.row, endPos.column));
								AutoCompleteData::RetriveContext(*autoComplete.Obj(), range, newContext.modifiedNode.Obj(), parsingExecutor.Obj());
							}
						}
						newContext.autoComplete = autoComplete;
					}
				}
			}

			void GuiGrammarAutoComplete::Execute(const RepeatingParsingOutput& input)
			{
				SPIN_LOCK(contextLock)
				{
					if(input.editVersion<context.input.editVersion)
					{
						return;
					}
				}
				AutoCompleteContext newContext;
				bool byGlobalCorrection=false;

				if(input.node)
				{
					newContext.input=input;
					ExecuteRefresh(newContext);
					byGlobalCorrection=true;
				}
				else
				{
					SPIN_LOCK(contextLock)
					{
						newContext=context;
						newContext.modifiedNode=0;
						newContext.autoComplete=0;
					}
					if(newContext.originalNode)
					{
						ExecuteEdit(newContext);
					}
				}

				if(newContext.originalNode)
				{
					ExecuteCalculateList(newContext);
				}

				SPIN_LOCK(contextLock)
				{
					context=newContext;
				}
				if(newContext.modifiedNode)
				{
					OnContextFinishedAsync(context);
					GetApplication()->InvokeInMainThread(ownerComposition->GetRelatedControlHost(), [=]()
					{
						PostList(newContext, byGlobalCorrection);
					});
				}
			}

			void GuiGrammarAutoComplete::PostList(const AutoCompleteContext& newContext, bool byGlobalCorrection)
			{
				bool openList = true;			// true: make the list visible
				bool keepListState = false;		// true: don't change the list visibility
				Ptr<AutoCompleteData> autoComplete = newContext.autoComplete;

				// if failed to get the auto complete list, close
				if (!autoComplete)
				{
					openList = false;
				}
				if (openList)
				{
					if (autoComplete->shownCandidates.Count() + autoComplete->candidateItems.Count() == 0)
					{
						openList = false;
					}
				}

				TextPos startPosition, endPosition;
				WString editingText;
				if (openList)
				{
					SPIN_LOCK(editTraceLock)
					{
						// if the edit version is invalid, cancel
						vint traceIndex = UnsafeGetEditTraceIndex(newContext.modifiedEditVersion);
						if (traceIndex == -1)
						{
							return;
						}
						// an edit version has two trace at most, for text change and caret change, here we peak the text change
						if (traceIndex > 0 && editTrace[traceIndex - 1].editVersion == context.modifiedEditVersion)
						{
							traceIndex--;
						}
						// if the edit version is not created by keyboard input, close
						if (traceIndex >= 0)
						{
							TextEditNotifyStruct& trace = editTrace[traceIndex];
							if (!trace.keyInput)
							{
								openList = false;
							}
						}

						// scan all traces from the calculation's edit version until now
						if (openList)
						{
							keepListState = true;
							startPosition = autoComplete->startPosition;
							endPosition = editTrace[editTrace.Count() - 1].inputEnd;
							for (vint i = traceIndex; i < editTrace.Count(); i++)
							{
								TextEditNotifyStruct& trace = editTrace[i];
								// if there are no text change trace until now, don't change the list
								if (trace.originalText != L"" || trace.inputText != L"")
								{
									keepListState = false;
								}
								// if the edit position goes before the start position of the auto complete, refresh
								if (trace.inputEnd <= startPosition)
								{
									openList = false;
									break;
								}
							}
						}

						if (traceIndex > 0)
						{
							editTrace.RemoveRange(0, traceIndex);
						}
					}
				}

				// if there is a global correction send to the UI thread but the list is not opening, cancel
				if (byGlobalCorrection && !IsListOpening())
				{
					return;
				}

				// if the input text from the start position to the current position crosses a token, close
				if (openList && element)
				{
					editingText = element->GetLines().GetText(startPosition, endPosition);
					if (grammarParser->GetTable()->GetLexer().Walk().IsClosedToken(editingText))
					{
						openList = false;
					}
				}

				// calculate the content of the list
				if (autoComplete && ((!keepListState && openList) || IsListOpening()))
				{
					SortedList<WString> itemKeys;
					List<ParsingCandidateItem> itemValues;

					// copy all candidate keywords
					FOREACH(vint, token, autoComplete->shownCandidates)
					{
						WString literal = parsingExecutor->GetTokenMetaData(token).unescapedRegexText;
						if (literal != L"" && !itemKeys.Contains(literal))
						{
							ParsingCandidateItem item;
							item.name = literal;
							item.semanticId = -1;
							itemValues.Insert(itemKeys.Add(literal), item);
						}
					}

					// copy all candidate symbols
					if (autoComplete->acceptableSemanticIds)
					{
						FOREACH(ParsingCandidateItem, item, autoComplete->candidateItems)
						{
							if (autoComplete->acceptableSemanticIds->Contains(item.semanticId))
							{
								// add all acceptable display of a symbol
								// because a symbol can has multiple representation in different places
								if (item.name != L"" && !itemKeys.Contains(item.name))
								{
									itemValues.Insert(itemKeys.Add(item.name), item);
								}
							}
						}
					}

					// fill the list
					List<GuiTextBoxAutoCompleteBase::AutoCompleteItem> candidateItems;
					for (vint i = 0; i < itemValues.Count(); i++)
					{
						auto& item = itemValues[i];
						if (item.tag.IsNull())
						{
							if (auto analyzer = parsingExecutor->GetAnalyzer())
							{
								item.tag = analyzer->CreateTagForCandidateItem(item);
							}
						}

						GuiTextBoxAutoCompleteBase::AutoCompleteItem candidateItem;
						candidateItem.text = item.name;
						candidateItem.tag = item.tag;
						candidateItems.Add(candidateItem);
					}
					SetListContent(candidateItems);
				}

				// set the list state
				if (!keepListState)
				{
					if (openList)
					{
						OpenList(startPosition);
					}
					else
					{
						CloseList();
					}
				}

				if (IsListOpening())
				{
					HighlightList(editingText);
				}
			}

			void GuiGrammarAutoComplete::Initialize()
			{
				grammarParser=CreateAutoRecoverParser(parsingExecutor->GetParser()->GetTable());
				CollectLeftRecursiveRules();
				parsingExecutor->AttachCallback(this);
			}

			void GuiGrammarAutoComplete::OnContextFinishedAsync(AutoCompleteContext& context)
			{
				if (auto analyzer = parsingExecutor->GetAnalyzer())
				{
					if (context.autoComplete && context.autoComplete->acceptableSemanticIds)
					{
						analyzer->GetCandidateItemsAsync(*context.autoComplete.Obj(), context, context.autoComplete->candidateItems);
					}
				}
			}

			void GuiGrammarAutoComplete::EnsureAutoCompleteFinished()
			{
				parsingExecutor->EnsureTaskFinished();
				SPIN_LOCK(contextLock)
				{
					context = AutoCompleteContext();
				}
			}

			GuiGrammarAutoComplete::GuiGrammarAutoComplete(Ptr<RepeatingParsingExecutor> _parsingExecutor)
				:RepeatingParsingExecutor::CallbackBase(_parsingExecutor)
				,editing(false)
			{
				Initialize();
			}

			GuiGrammarAutoComplete::GuiGrammarAutoComplete(Ptr<parsing::tabling::ParsingGeneralParser> _grammarParser, const WString& _grammarRule)
				:RepeatingParsingExecutor::CallbackBase(new RepeatingParsingExecutor(_grammarParser, _grammarRule))
				,editing(false)
			{
				Initialize();
			}

			GuiGrammarAutoComplete::~GuiGrammarAutoComplete()
			{
				EnsureAutoCompleteFinished();
				parsingExecutor->DetachCallback(this);
			}

			Ptr<RepeatingParsingExecutor> GuiGrammarAutoComplete::GetParsingExecutor()
			{
				return parsingExecutor;
			}
		}
	}
}

/***********************************************************************
.\CONTROLS\TEXTEDITORPACKAGE\LANGUAGESERVICE\GUILANGUAGEOPERATIONS.CPP
***********************************************************************/

namespace vl
{
	namespace presentation
	{
		namespace controls
		{
			using namespace collections;
			using namespace parsing;
			using namespace parsing::tabling;
			using namespace regex_internal;

/***********************************************************************
ParsingContext
***********************************************************************/

			bool ParsingTokenContext::RetriveContext(ParsingTokenContext& output, parsing::ParsingTreeNode* foundNode, RepeatingParsingExecutor* executor)
			{
				ParsingTreeToken* foundToken=dynamic_cast<ParsingTreeToken*>(foundNode);
				if(!foundToken) return false;
				ParsingTreeObject* tokenParent=dynamic_cast<ParsingTreeObject*>(foundNode->GetParent());
				if(!tokenParent) return false;
				vint index=tokenParent->GetMembers().Values().IndexOf(foundNode);
				if(index==-1) return false;

				WString type=tokenParent->GetType();
				WString field=tokenParent->GetMembers().Keys().Get(index);
				const RepeatingParsingExecutor::FieldMetaData& md=executor->GetFieldMetaData(type, field);

				output.foundToken=foundToken;
				output.tokenParent=tokenParent;
				output.type=type;
				output.field=field;
				output.acceptableSemanticIds=md.semantics;
				return true;
			}

			bool ParsingTokenContext::RetriveContext(ParsingTokenContext& output, parsing::ParsingTextPos pos, parsing::ParsingTreeObject* rootNode, RepeatingParsingExecutor* executor)
			{
				ParsingTreeNode* foundNode=rootNode->FindDeepestNode(pos);
				if(!foundNode) return false;
				return RetriveContext(output, foundNode, executor);
			}

			bool ParsingTokenContext::RetriveContext(ParsingTokenContext& output, parsing::ParsingTextRange range, ParsingTreeObject* rootNode, RepeatingParsingExecutor* executor)
			{
				ParsingTreeNode* foundNode=rootNode->FindDeepestNode(range);
				if(!foundNode) return false;
				return RetriveContext(output, foundNode, executor);
			}

/***********************************************************************
RepeatingParsingExecutor::IParsingAnalyzer
***********************************************************************/

		parsing::ParsingTreeNode* RepeatingParsingExecutor::IParsingAnalyzer::ToParent(parsing::ParsingTreeNode* node, const RepeatingPartialParsingOutput* output)
		{
			if (!output || !output->modifiedNode) return node;
			return node == output->modifiedNode.Obj()
				? output->originalNode.Obj()
				: node;
		}

		parsing::ParsingTreeObject* RepeatingParsingExecutor::IParsingAnalyzer::ToChild(parsing::ParsingTreeObject* node, const RepeatingPartialParsingOutput* output)
		{
			if (!output || !output->modifiedNode) return node;
			return node == output->originalNode.Obj()
				? output->modifiedNode.Obj()
				: node;
		}

		Ptr<parsing::ParsingTreeNode> RepeatingParsingExecutor::IParsingAnalyzer::ToChild(Ptr<parsing::ParsingTreeNode> node, const RepeatingPartialParsingOutput* output)
		{
			if (!output) return node;
			return node == output->originalNode
				? output->modifiedNode.Cast<ParsingTreeNode>()
				: node;
		}

		parsing::ParsingTreeNode* RepeatingParsingExecutor::IParsingAnalyzer::GetParent(parsing::ParsingTreeNode* node, const RepeatingPartialParsingOutput* output)
		{
			return ToParent(node, output)->GetParent();
		}

		Ptr<parsing::ParsingTreeNode> RepeatingParsingExecutor::IParsingAnalyzer::GetMember(parsing::ParsingTreeObject* node, const WString& name, const RepeatingPartialParsingOutput* output)
		{
			return ToChild(ToChild(node, output)->GetMember(name), output);
		}

		Ptr<parsing::ParsingTreeNode> RepeatingParsingExecutor::IParsingAnalyzer::GetItem(parsing::ParsingTreeArray* node, vint index, const RepeatingPartialParsingOutput* output)
		{
			return ToChild(node->GetItem(index), output);
		}

/***********************************************************************
RepeatingParsingExecutor::CallbackBase
***********************************************************************/

			RepeatingParsingExecutor::CallbackBase::CallbackBase(Ptr<RepeatingParsingExecutor> _parsingExecutor)
				:parsingExecutor(_parsingExecutor)
				,callbackAutoPushing(false)
				,callbackElement(0)
				,callbackElementModifyLock(0)
			{
			}

			RepeatingParsingExecutor::CallbackBase::~CallbackBase()
			{
			}

			void RepeatingParsingExecutor::CallbackBase::RequireAutoSubmitTask(bool enabled)
			{
				callbackAutoPushing=enabled;
			}

			void RepeatingParsingExecutor::CallbackBase::Attach(elements::GuiColorizedTextElement* _element, SpinLock& _elementModifyLock, compositions::GuiGraphicsComposition* _ownerComposition, vuint editVersion)
			{
				if(_element)
				{
					SPIN_LOCK(_elementModifyLock)
					{
						callbackElement=_element;
						callbackElementModifyLock=&_elementModifyLock;
					}
				}
				
				parsingExecutor->ActivateCallback(this);
				if(callbackElement && callbackElementModifyLock && callbackAutoPushing)
				{
					SPIN_LOCK(*callbackElementModifyLock)
					{
						RepeatingParsingInput input;
						input.editVersion=editVersion;
						input.code=callbackElement->GetLines().GetText();
						parsingExecutor->SubmitTask(input);
					}
				}
			}

			void RepeatingParsingExecutor::CallbackBase::Detach()
			{
				if(callbackElement && callbackElementModifyLock)
				{
					SPIN_LOCK(*callbackElementModifyLock)
					{
						callbackElement=0;
						callbackElementModifyLock=0;
					}
				}
				
				parsingExecutor->DeactivateCallback(this);
			}

			void RepeatingParsingExecutor::CallbackBase::TextEditPreview(TextEditPreviewStruct& arguments)
			{
			}

			void RepeatingParsingExecutor::CallbackBase::TextEditNotify(const TextEditNotifyStruct& arguments)
			{
			}

			void RepeatingParsingExecutor::CallbackBase::TextCaretChanged(const TextCaretChangedStruct& arguments)
			{
			}

			void RepeatingParsingExecutor::CallbackBase::TextEditFinished(vuint editVersion)
			{
				if(callbackElement && callbackElementModifyLock && callbackAutoPushing)
				{
					SPIN_LOCK(*callbackElementModifyLock)
					{
						RepeatingParsingInput input;
						input.editVersion=editVersion;
						input.code=callbackElement->GetLines().GetText();
						parsingExecutor->SubmitTask(input);
					}
				}
			}

/***********************************************************************
RepeatingParsingExecutor
***********************************************************************/

			void RepeatingParsingExecutor::Execute(const RepeatingParsingInput& input)
			{
				List<Ptr<ParsingError>> errors;
				Ptr<ParsingTreeObject> node=grammarParser->Parse(input.code, grammarRule, errors).Cast<ParsingTreeObject>();
				if(node)
				{
					node->InitializeQueryCache();
				}

				RepeatingParsingOutput result;
				result.node=node;
				result.editVersion=input.editVersion;
				result.code=input.code;
				if(node)
				{
					OnContextFinishedAsync(result);
					FOREACH(ICallback*, callback, callbacks)
					{
						callback->OnParsingFinishedAsync(result);
					}
				}
			}

			void RepeatingParsingExecutor::PrepareMetaData()
			{
				Ptr<ParsingTable> table=grammarParser->GetTable();
				tokenIndexMap.Clear();
				semanticIndexMap.Clear();
				tokenMetaDatas.Clear();
				fieldMetaDatas.Clear();

				Dictionary<vint, Ptr<ParsingTable::AttributeInfo>> tokenColorAtts, tokenContextColorAtts, tokenCandidateAtts, tokenAutoCompleteAtts;
				Dictionary<FieldDesc, Ptr<ParsingTable::AttributeInfo>> fieldColorAtts, fieldSemanticAtts;

				{
					vint tokenCount=table->GetTokenCount();
					for(vint token=ParsingTable::UserTokenStart;token<tokenCount;token++)
					{
						const ParsingTable::TokenInfo& tokenInfo=table->GetTokenInfo(token);
						vint tokenIndex=token-ParsingTable::UserTokenStart;
						tokenIndexMap.Add(tokenInfo.name, tokenIndex);

						if(Ptr<ParsingTable::AttributeInfo> att=GetColorAttribute(tokenInfo.attributeIndex))
						{
							tokenColorAtts.Add(tokenIndex, att);
						}
						if(Ptr<ParsingTable::AttributeInfo> att=GetContextColorAttribute(tokenInfo.attributeIndex))
						{
							tokenContextColorAtts.Add(tokenIndex, att);
						}
						if(Ptr<ParsingTable::AttributeInfo> att=GetCandidateAttribute(tokenInfo.attributeIndex))
						{
							tokenCandidateAtts.Add(tokenIndex, att);
						}
						if(Ptr<ParsingTable::AttributeInfo> att=GetAutoCompleteAttribute(tokenInfo.attributeIndex))
						{
							tokenAutoCompleteAtts.Add(tokenIndex, att);
						}
					}
				}
				{
					vint fieldCount=table->GetTreeFieldInfoCount();
					for(vint field=0;field<fieldCount;field++)
					{
						const ParsingTable::TreeFieldInfo& fieldInfo=table->GetTreeFieldInfo(field);
						FieldDesc fieldDesc(fieldInfo.type, fieldInfo.field);

						if(Ptr<ParsingTable::AttributeInfo> att=GetColorAttribute(fieldInfo.attributeIndex))
						{
							fieldColorAtts.Add(fieldDesc, att);
						}
						if(Ptr<ParsingTable::AttributeInfo> att=GetSemanticAttribute(fieldInfo.attributeIndex))
						{
							fieldSemanticAtts.Add(fieldDesc, att);
						}
					}
				}

				FOREACH(Ptr<ParsingTable::AttributeInfo>, att, 
					From(tokenColorAtts.Values())
						.Concat(tokenContextColorAtts.Values())
						.Concat(fieldColorAtts.Values())
						.Concat(fieldSemanticAtts.Values())
					)
				{
					FOREACH(WString, argument, att->arguments)
					{
						if(!semanticIndexMap.Contains(argument))
						{
							semanticIndexMap.Add(argument);
						}
					}
				}

				vint index=0;
				FOREACH(vint, tokenIndex, tokenIndexMap.Values())
				{
					TokenMetaData md;
					md.tableTokenIndex=tokenIndex+ParsingTable::UserTokenStart;
					md.lexerTokenIndex=tokenIndex;
					md.defaultColorIndex=-1;
					md.hasContextColor=false;
					md.hasAutoComplete=false;
					md.isCandidate=false;

					if((index=tokenColorAtts.Keys().IndexOf(tokenIndex))!=-1)
					{
						md.defaultColorIndex=semanticIndexMap.IndexOf(tokenColorAtts.Values()[index]->arguments[0]);
					}
					md.hasContextColor=tokenContextColorAtts.Keys().Contains(tokenIndex);
					md.hasAutoComplete=tokenAutoCompleteAtts.Keys().Contains(tokenIndex);
					if((md.isCandidate=tokenCandidateAtts.Keys().Contains(tokenIndex)))
					{
						const ParsingTable::TokenInfo& tokenInfo=table->GetTokenInfo(md.tableTokenIndex);
						if(IsRegexEscapedLiteralString(tokenInfo.regex))
						{
							md.unescapedRegexText=UnescapeTextForRegex(tokenInfo.regex);
						}
						else
						{
							md.isCandidate=false;
						}
					}

					tokenMetaDatas.Add(tokenIndex, md);
				}
				{
					vint fieldCount=table->GetTreeFieldInfoCount();
					for(vint field=0;field<fieldCount;field++)
					{
						const ParsingTable::TreeFieldInfo& fieldInfo=table->GetTreeFieldInfo(field);
						FieldDesc fieldDesc(fieldInfo.type, fieldInfo.field);

						FieldMetaData md;
						md.colorIndex=-1;

						if((index=fieldColorAtts.Keys().IndexOf(fieldDesc))!=-1)
						{
							md.colorIndex=semanticIndexMap.IndexOf(fieldColorAtts.Values()[index]->arguments[0]);
						}
						if((index=fieldSemanticAtts.Keys().IndexOf(fieldDesc))!=-1)
						{
							md.semantics=new List<vint>;
							FOREACH(WString, argument, fieldSemanticAtts.Values()[index]->arguments)
							{
								md.semantics->Add(semanticIndexMap.IndexOf(argument));
							}
						}

						fieldMetaDatas.Add(fieldDesc, md);
					}
				}
			}

			void RepeatingParsingExecutor::OnContextFinishedAsync(RepeatingParsingOutput& context)
			{
				if(analyzer)
				{
					context.cache = analyzer->CreateCacheAsync(context);
				}
			}

			RepeatingParsingExecutor::RepeatingParsingExecutor(Ptr<parsing::tabling::ParsingGeneralParser> _grammarParser, const WString& _grammarRule, Ptr<IParsingAnalyzer> _analyzer)
				:grammarParser(_grammarParser)
				,grammarRule(_grammarRule)
				,analyzer(_analyzer)
				,autoPushingCallback(0)
			{
				PrepareMetaData();
				if (analyzer)
				{
					analyzer->Attach(this);
				}
			}

			RepeatingParsingExecutor::~RepeatingParsingExecutor()
			{
				EnsureTaskFinished();
				if (analyzer)
				{
					analyzer->Detach(this);
				}
			}

			Ptr<parsing::tabling::ParsingGeneralParser> RepeatingParsingExecutor::GetParser()
			{
				return grammarParser;
			}

			bool RepeatingParsingExecutor::AttachCallback(ICallback* value)
			{
				if(!value) return false;
				if(callbacks.Contains(value)) return false;
				callbacks.Add(value);
				return true;
			}

			bool RepeatingParsingExecutor::DetachCallback(ICallback* value)
			{
				if(!value) return false;
				if(!callbacks.Contains(value)) return false;
				DeactivateCallback(value);
				callbacks.Remove(value);
				return true;
			}

			bool RepeatingParsingExecutor::ActivateCallback(ICallback* value)
			{
				if(!value) return false;
				if(!callbacks.Contains(value)) return false;
				if(activatedCallbacks.Contains(value)) return false;
				activatedCallbacks.Add(value);

				if(!autoPushingCallback)
				{
					autoPushingCallback=value;
					autoPushingCallback->RequireAutoSubmitTask(true);
				}
				return true;
			}

			bool RepeatingParsingExecutor::DeactivateCallback(ICallback* value)
			{
				if(!value) return false;
				if(!callbacks.Contains(value)) return false;
				if(!activatedCallbacks.Contains(value)) return false;

				if(autoPushingCallback==value)
				{
					autoPushingCallback->RequireAutoSubmitTask(false);
					autoPushingCallback=0;
				}
				activatedCallbacks.Remove(value);
				if(!autoPushingCallback && activatedCallbacks.Count()>0)
				{
					autoPushingCallback=activatedCallbacks[0];
					autoPushingCallback->RequireAutoSubmitTask(true);
				}
				return true;
			}

			Ptr<RepeatingParsingExecutor::IParsingAnalyzer> RepeatingParsingExecutor::GetAnalyzer()
			{
				return analyzer;
			}

			vint RepeatingParsingExecutor::GetTokenIndex(const WString& tokenName)
			{
				vint index=tokenIndexMap.Keys().IndexOf(tokenName);
				return index==-1?-1:tokenIndexMap.Values()[index];
			}

			vint RepeatingParsingExecutor::GetSemanticId(const WString& name)
			{
				return semanticIndexMap.IndexOf(name);
			}

			WString RepeatingParsingExecutor::GetSemanticName(vint id)
			{
				return 0<=id&&id<semanticIndexMap.Count()?semanticIndexMap[id]:L"";
			}

			const RepeatingParsingExecutor::TokenMetaData& RepeatingParsingExecutor::GetTokenMetaData(vint regexTokenIndex)
			{
				return tokenMetaDatas[regexTokenIndex];
			}

			const RepeatingParsingExecutor::FieldMetaData& RepeatingParsingExecutor::GetFieldMetaData(const WString& type, const WString& field)
			{
				return fieldMetaDatas[FieldDesc(type, field)];
			}

			Ptr<parsing::tabling::ParsingTable::AttributeInfo> RepeatingParsingExecutor::GetAttribute(vint index, const WString& name, vint argumentCount)
			{
				if(index!=-1)
				{
					Ptr<ParsingTable::AttributeInfo> att=grammarParser->GetTable()->GetAttributeInfo(index)->FindFirst(name);
					if(att && (argumentCount==-1 || att->arguments.Count()==argumentCount))
					{
						return att;
					}
				}
				return 0;
			}

			Ptr<parsing::tabling::ParsingTable::AttributeInfo> RepeatingParsingExecutor::GetColorAttribute(vint index)
			{
				return GetAttribute(index, L"Color", 1);
			}

			Ptr<parsing::tabling::ParsingTable::AttributeInfo> RepeatingParsingExecutor::GetContextColorAttribute(vint index)
			{
				return GetAttribute(index, L"ContextColor", 0);
			}

			Ptr<parsing::tabling::ParsingTable::AttributeInfo> RepeatingParsingExecutor::GetSemanticAttribute(vint index)
			{
				return GetAttribute(index, L"Semantic", -1);
			}

			Ptr<parsing::tabling::ParsingTable::AttributeInfo> RepeatingParsingExecutor::GetCandidateAttribute(vint index)
			{
				return GetAttribute(index, L"Candidate", 0);
			}

			Ptr<parsing::tabling::ParsingTable::AttributeInfo> RepeatingParsingExecutor::GetAutoCompleteAttribute(vint index)
			{
				return GetAttribute(index, L"AutoComplete", 0);
			}
		}
	}
}

/***********************************************************************
.\CONTROLS\TEXTEDITORPACKAGE\LANGUAGESERVICE\GUILANGUAGECOLORIZER.CPP
***********************************************************************/

namespace vl
{
	namespace presentation
	{
		namespace controls
		{
			using namespace elements;
			using namespace parsing;
			using namespace parsing::tabling;
			using namespace collections;
			using namespace theme;

/***********************************************************************
GuiGrammarColorizer
***********************************************************************/

			void GuiGrammarColorizer::OnParsingFinishedAsync(const RepeatingParsingOutput& output)
			{
				SPIN_LOCK(contextLock)
				{
					context=output;
					OnContextFinishedAsync(context);
				}
				RestartColorizer();
			}

			void GuiGrammarColorizer::OnContextFinishedAsync(const RepeatingParsingOutput& context)
			{
			}

			void GuiGrammarColorizer::Attach(elements::GuiColorizedTextElement* _element, SpinLock& _elementModifyLock, compositions::GuiGraphicsComposition* _ownerComposition, vuint editVersion)
			{
				GuiTextBoxRegexColorizer::Attach(_element, _elementModifyLock, _ownerComposition, editVersion);
				RepeatingParsingExecutor::CallbackBase::Attach(_element, _elementModifyLock, _ownerComposition, editVersion);
			}

			void GuiGrammarColorizer::Detach()
			{
				GuiTextBoxRegexColorizer::Detach();
				RepeatingParsingExecutor::CallbackBase::Detach();
				if(element && elementModifyLock)
				{
					parsingExecutor->EnsureTaskFinished();
					StopColorizer(false);
				}
			}

			void GuiGrammarColorizer::TextEditPreview(TextEditPreviewStruct& arguments)
			{
				GuiTextBoxRegexColorizer::TextEditPreview(arguments);
				RepeatingParsingExecutor::CallbackBase::TextEditPreview(arguments);
			}

			void GuiGrammarColorizer::TextEditNotify(const TextEditNotifyStruct& arguments)
			{
				GuiTextBoxRegexColorizer::TextEditNotify(arguments);
				RepeatingParsingExecutor::CallbackBase::TextEditNotify(arguments);
			}

			void GuiGrammarColorizer::TextCaretChanged(const TextCaretChangedStruct& arguments)
			{
				GuiTextBoxRegexColorizer::TextCaretChanged(arguments);
				RepeatingParsingExecutor::CallbackBase::TextCaretChanged(arguments);
			}

			void GuiGrammarColorizer::TextEditFinished(vuint editVersion)
			{
				GuiTextBoxRegexColorizer::TextEditFinished(editVersion);
				RepeatingParsingExecutor::CallbackBase::TextEditFinished(editVersion);
			}

			void GuiGrammarColorizer::OnSemanticColorize(SemanticColorizeContext& context, const RepeatingParsingOutput& input)
			{
				if (auto analyzer = parsingExecutor->GetAnalyzer())
				{
					auto semanticId = analyzer->GetSemanticIdForTokenAsync(context, input);
					if(semanticId!=-1)
					{
						context.semanticId=semanticId;
					}
				}
			}

			void GuiGrammarColorizer::EnsureColorizerFinished()
			{
				parsingExecutor->EnsureTaskFinished();
				StopColorizerForever();
				SPIN_LOCK(contextLock)
				{
					context=RepeatingParsingOutput();
				}
			}

			GuiGrammarColorizer::GuiGrammarColorizer(Ptr<RepeatingParsingExecutor> _parsingExecutor)
				:RepeatingParsingExecutor::CallbackBase(_parsingExecutor)
			{
				parsingExecutor->AttachCallback(this);
				BeginSetColors();
			}

			GuiGrammarColorizer::GuiGrammarColorizer(Ptr<parsing::tabling::ParsingGeneralParser> _grammarParser, const WString& _grammarRule)
				:RepeatingParsingExecutor::CallbackBase(new RepeatingParsingExecutor(_grammarParser, _grammarRule))
			{
				parsingExecutor->AttachCallback(this);
				BeginSetColors();
			}

			GuiGrammarColorizer::~GuiGrammarColorizer()
			{
				EnsureColorizerFinished();
				parsingExecutor->DetachCallback(this);
			}

			void GuiGrammarColorizer::BeginSetColors()
			{
				ClearTokens();
				colorSettings.Clear();

				text::ColorEntry entry;
				{
					entry.normal.text = Color(0, 0, 0);
					entry.normal.background = Color(0, 0, 0, 0);
					entry.selectedFocused.text = Color(255, 255, 255);
					entry.selectedFocused.background = Color(51, 153, 255);
					entry.selectedUnfocused.text = Color(255, 255, 255);
					entry.selectedUnfocused.background = Color(51, 153, 255);
				}

				SetDefaultColor(entry);
				colorSettings.Add(L"Default", entry);
			}

			const collections::SortedList<WString>& GuiGrammarColorizer::GetColorNames()
			{
				return colorSettings.Keys();
			}

			GuiGrammarColorizer::ColorEntry GuiGrammarColorizer::GetColor(const WString& name)
			{
				vint index=colorSettings.Keys().IndexOf(name);
				return index==-1?GetDefaultColor():colorSettings.Values().Get(index);
			}

			void GuiGrammarColorizer::SetColor(const WString& name, const ColorEntry& entry)
			{
				colorSettings.Set(name, entry);
			}

			void GuiGrammarColorizer::SetColor(const WString& name, const Color& color)
			{
				text::ColorEntry entry=GetDefaultColor();
				entry.normal.text=color;
				SetColor(name, entry);
			}

			void GuiGrammarColorizer::EndSetColors()
			{
				SortedList<WString> tokenColors;
				Ptr<ParsingTable> table=parsingExecutor->GetParser()->GetTable();
				semanticColorMap.Clear();

				vint tokenCount=table->GetTokenCount();
				for(vint token=ParsingTable::UserTokenStart;token<tokenCount;token++)
				{
					const ParsingTable::TokenInfo& tokenInfo=table->GetTokenInfo(token);
					const RepeatingParsingExecutor::TokenMetaData& md=parsingExecutor->GetTokenMetaData(token-ParsingTable::UserTokenStart);
					if(md.defaultColorIndex==-1)
					{
						AddToken(tokenInfo.regex, GetDefaultColor());
					}
					else
					{
						WString name=parsingExecutor->GetSemanticName(md.defaultColorIndex);
						vint color=AddToken(tokenInfo.regex, GetColor(name));
						semanticColorMap.Set(md.defaultColorIndex, color);
						tokenColors.Add(name);
					}
				}

				FOREACH_INDEXER(WString, color, index, colorSettings.Keys())
				{
					if(!tokenColors.Contains(color))
					{
						vint semanticId=parsingExecutor->GetSemanticId(color);
						if(semanticId!=-1)
						{
							vint tokenId=AddExtraToken(colorSettings.Values().Get(index));
							vint color=tokenId+tokenCount-ParsingTable::UserTokenStart;
							semanticColorMap.Set(semanticId, color);
						}
					}
				}
				Setup();
			}

			void GuiGrammarColorizer::ColorizeTokenContextSensitive(vint lineIndex, const wchar_t* text, vint start, vint length, vint& token, vint& contextState)
			{
				SPIN_LOCK(contextLock)
				{
					ParsingTreeObject* node=context.node.Obj();
					if(node && token!=-1 && parsingExecutor->GetTokenMetaData(token).hasContextColor)
					{
						ParsingTextPos pos(lineIndex, start);
						SemanticColorizeContext scContext;
						if(SemanticColorizeContext::RetriveContext(scContext, pos, node, parsingExecutor.Obj()))
						{
							const RepeatingParsingExecutor::FieldMetaData& md=parsingExecutor->GetFieldMetaData(scContext.type, scContext.field);
							vint semantic=md.colorIndex;
							scContext.semanticId=-1;

							if(scContext.acceptableSemanticIds)
							{
								OnSemanticColorize(scContext, context);
								if(md.semantics->Contains(scContext.semanticId))
								{
									semantic=scContext.semanticId;
								}
							}

							if(semantic!=-1)
							{
								vint index=semanticColorMap.Keys().IndexOf(semantic);
								if(index!=-1)
								{
									token=semanticColorMap.Values()[index];
								}
							}
						}
					}
				}
			}

			Ptr<RepeatingParsingExecutor> GuiGrammarColorizer::GetParsingExecutor()
			{
				return parsingExecutor;
			}
		}
	}
}

/***********************************************************************
.\CONTROLS\TOOLSTRIPPACKAGE\GUIRIBBONCONTROLS.CPP
***********************************************************************/

namespace vl
{
	namespace presentation
	{
		namespace controls
		{
			using namespace reflection::description;
			using namespace collections;
			using namespace compositions;
			using namespace theme;
			using namespace templates;

/***********************************************************************
GuiRibbonTab
***********************************************************************/

			void GuiRibbonTab::BeforeControlTemplateUninstalled_()
			{
				auto ct = GetControlTemplateObject(false);
				if (!ct) return;

				if (auto bhc = ct->GetBeforeHeadersContainer())
				{
					bhc->RemoveChild(beforeHeaders);
				}
				if (auto ahc = ct->GetAfterHeadersContainer())
				{
					ahc->RemoveChild(afterHeaders);
				}
			}

			void GuiRibbonTab::AfterControlTemplateInstalled_(bool initialize)
			{
				auto ct = GetControlTemplateObject(true);
				if (auto bhc = ct->GetBeforeHeadersContainer())
				{
					bhc->AddChild(beforeHeaders);
				}
				if (auto ahc = ct->GetAfterHeadersContainer())
				{
					ahc->AddChild(afterHeaders);
				}
			}

			GuiRibbonTab::GuiRibbonTab(theme::ThemeName themeName)
				:GuiTab(themeName)
			{
				beforeHeaders = new GuiBoundsComposition();
				beforeHeaders->SetAlignmentToParent(Margin(0, 0, 0, 0));
				beforeHeaders->SetMinSizeLimitation(GuiGraphicsComposition::LimitToElementAndChildren);

				afterHeaders = new GuiBoundsComposition();
				afterHeaders->SetAlignmentToParent(Margin(0, 0, 0, 0));
				afterHeaders->SetMinSizeLimitation(GuiGraphicsComposition::LimitToElementAndChildren);
			}

			GuiRibbonTab::~GuiRibbonTab()
			{
				if (!beforeHeaders->GetParent())
				{
					SafeDeleteComposition(beforeHeaders);
				}
				if (!afterHeaders->GetParent())
				{
					SafeDeleteComposition(afterHeaders);
				}
			}

			compositions::GuiGraphicsComposition* GuiRibbonTab::GetBeforeHeaders()
			{
				return beforeHeaders;
			}

			compositions::GuiGraphicsComposition* GuiRibbonTab::GetAfterHeaders()
			{
				return afterHeaders;
			}

/***********************************************************************
GuiRibbonGroupCollection
***********************************************************************/

			bool GuiRibbonGroupCollection::QueryInsert(vint index, GuiRibbonGroup* const& value)
			{
				return !value->GetBoundsComposition()->GetParent();
			}

			void GuiRibbonGroupCollection::AfterInsert(vint index, GuiRibbonGroup* const& value)
			{
				value->GetBoundsComposition()->SetAlignmentToParent(Margin(0, 0, 0, 0));

				auto item = new GuiStackItemComposition();
				item->AddChild(value->GetBoundsComposition());

				tabPage->stack->InsertStackItem(index, item);
			}

			void GuiRibbonGroupCollection::AfterRemove(vint index, vint count)
			{
				for (vint i = 0; i < count; i++)
				{
					auto item = tabPage->stack->GetStackItems()[index];
					tabPage->stack->RemoveChild(item);

					item->RemoveChild(item->Children()[0]);
					delete item;
				}
			}

			GuiRibbonGroupCollection::GuiRibbonGroupCollection(GuiRibbonTabPage* _tabPage)
				:tabPage(_tabPage)
			{
			}

			GuiRibbonGroupCollection::~GuiRibbonGroupCollection()
			{
			}

/***********************************************************************
GuiRibbonTabPage
***********************************************************************/

			GuiRibbonTabPage::GuiRibbonTabPage(theme::ThemeName themeName)
				:GuiTabPage(themeName)
				, groups(this)
			{
				stack = new GuiStackComposition();
				stack->SetDirection(GuiStackComposition::Horizontal);
				stack->SetAlignmentToParent(Margin(2, 2, 2, 2));
				stack->SetPadding(2);
				stack->SetMinSizeLimitation(GuiGraphicsComposition::LimitToElementAndChildren);

				responsiveStack = new GuiResponsiveStackComposition();
				responsiveStack->SetDirection(ResponsiveDirection::Horizontal);
				responsiveStack->SetAlignmentToParent(Margin(0, 0, 0, 0));
				responsiveStack->AddChild(stack);

				responsiveContainer = new GuiResponsiveContainerComposition();
				responsiveContainer->SetAlignmentToParent(Margin(0, 0, 0, 0));
				responsiveContainer->SetResponsiveTarget(responsiveStack);

				containerComposition->AddChild(responsiveContainer);

				HighlightedChanged.SetAssociatedComposition(boundsComposition);
			}

			GuiRibbonTabPage::~GuiRibbonTabPage()
			{
			}

			bool GuiRibbonTabPage::GetHighlighted()
			{
				return highlighted;
			}

			void GuiRibbonTabPage::SetHighlighted(bool value)
			{
				if (highlighted != value)
				{
					highlighted = value;
					HighlightedChanged.Execute(GetNotifyEventArguments());
				}
			}

			collections::ObservableListBase<GuiRibbonGroup*>& GuiRibbonTabPage::GetGroups()
			{
				return groups;
			}

/***********************************************************************
GuiRibbonGroupItemCollection
***********************************************************************/

			bool GuiRibbonGroupItemCollection::QueryInsert(vint index, GuiControl* const& value)
			{
				return !value->GetBoundsComposition()->GetParent();
			}

			void GuiRibbonGroupItemCollection::AfterInsert(vint index, GuiControl* const& value)
			{
				value->GetBoundsComposition()->SetAlignmentToParent(Margin(0, 0, 0, 0));

				auto item = new GuiStackItemComposition();
				item->AddChild(value->GetBoundsComposition());

				group->stack->InsertStackItem(index, item);
			}

			void GuiRibbonGroupItemCollection::AfterRemove(vint index, vint count)
			{
				for (vint i = 0; i < count; i++)
				{
					auto item = group->stack->GetStackItems()[index];
					group->stack->RemoveChild(item);

					item->RemoveChild(item->Children()[0]);
					delete item;
				}
			}

			GuiRibbonGroupItemCollection::GuiRibbonGroupItemCollection(GuiRibbonGroup* _group)
				:group(_group)
			{
			}

			GuiRibbonGroupItemCollection::~GuiRibbonGroupItemCollection()
			{
			}

/***********************************************************************
GuiRibbonGroup::CommandExecutor
***********************************************************************/

			GuiRibbonGroup::CommandExecutor::CommandExecutor(GuiRibbonGroup* _group)
				:group(_group)
			{
			}

			GuiRibbonGroup::CommandExecutor::~CommandExecutor()
			{
			}

			void GuiRibbonGroup::CommandExecutor::NotifyExpandButtonClicked()
			{
				group->ExpandButtonClicked.Execute(group->GetNotifyEventArguments());
			}

/***********************************************************************
GuiRibbonGroupMenu
***********************************************************************/

			class GuiRibbonGroupMenu : public GuiMenu, public Description<GuiRibbonGroupMenu>
			{
			private:
				IGuiMenuService::Direction GetPreferredDirection()override
				{
					return IGuiMenuService::Horizontal;
				}

				bool IsSubMenuActivatedByMouseDown()override
				{
					return true;
				}

			public:
				GuiRibbonGroupMenu(theme::ThemeName themeName, GuiControl* _owner)
					:GuiMenu(themeName, _owner)
				{
				}
			};

/***********************************************************************
GuiRibbonGroup
***********************************************************************/

			void GuiRibbonGroup::BeforeControlTemplateUninstalled_()
			{
				auto ct = GetControlTemplateObject(false);
				if (!ct) return;

				ct->SetCommands(nullptr);
			}

			void GuiRibbonGroup::AfterControlTemplateInstalled_(bool initialize)
			{
				auto ct = GetControlTemplateObject(true);
				ct->SetExpandable(expandable);
				ct->SetCollapsed(responsiveView->GetCurrentView() == responsiveFixedButton);
				ct->SetCommands(commandExecutor.Obj());
				dropdownButton->SetControlTemplate(ct->GetLargeDropdownButtonTemplate());
				dropdownMenu->SetControlTemplate(ct->GetSubMenuTemplate());
			}

			void GuiRibbonGroup::OnBoundsChanged(compositions::GuiGraphicsComposition* sender, compositions::GuiEventArgs& arguments)
			{
				dropdownMenu->GetBoundsComposition()->SetPreferredMinSize(Size(0, containerComposition->GetBounds().Height()));
			}

			void GuiRibbonGroup::OnTextChanged(compositions::GuiGraphicsComposition* sender, compositions::GuiEventArgs& arguments)
			{
				dropdownButton->SetText(GetText());
			}

			void GuiRibbonGroup::OnBeforeSubMenuOpening(compositions::GuiGraphicsComposition* sender, compositions::GuiEventArgs& arguments)
			{
				if (responsiveView->GetViews().Contains(responsiveFixedButton))
				{
					auto currentDropdown = dropdownMenu;
					if (items.Count() == 1)
					{
						if (auto provider = items[0]->QueryTypedService<IGuiMenuDropdownProvider>())
						{
							if (auto menu = provider->ProvideDropdownMenu())
							{
								currentDropdown = menu;
							}
						}
					}
					dropdownButton->SetSubMenu(currentDropdown, false);
				}
			}

			void GuiRibbonGroup::OnBeforeSwitchingView(compositions::GuiGraphicsComposition* sender, compositions::GuiItemEventArgs& arguments)
			{
				if (auto ct = GetControlTemplateObject(false))
				{
					ct->SetCollapsed(arguments.itemIndex == 1);
				}

				if (arguments.itemIndex == 0)
				{
					while (responsiveStack->LevelDown());
					dropdownMenu->GetContainerComposition()->RemoveChild(stack);
					responsiveStack->AddChild(stack);
					dropdownButton->SetSubMenu(nullptr, false);
				}
				else
				{
					while (responsiveStack->LevelUp());
					responsiveStack->RemoveChild(stack);
					dropdownMenu->GetContainerComposition()->AddChild(stack);
				}
			}

			GuiRibbonGroup::GuiRibbonGroup(theme::ThemeName themeName)
				:GuiControl(themeName)
				, items(this)
			{
				commandExecutor = new CommandExecutor(this);
				{
					stack = new GuiStackComposition();
					stack->SetDirection(GuiStackComposition::Horizontal);
					stack->SetAlignmentToParent(Margin(0, 0, 0, 0));
					stack->SetPadding(2);
					stack->SetMinSizeLimitation(GuiGraphicsComposition::LimitToElementAndChildren);

					responsiveStack = new GuiResponsiveStackComposition();
					responsiveStack->SetDirection(ResponsiveDirection::Horizontal);
					responsiveStack->SetAlignmentToParent(Margin(0, 0, 0, 0));
					responsiveStack->AddChild(stack);
				}
				{
					dropdownButton = new GuiToolstripButton(theme::ThemeName::RibbonLargeDropdownButton);
					dropdownButton->GetBoundsComposition()->SetAlignmentToParent(Margin(0, 0, 0, 0));

					responsiveFixedButton = new GuiResponsiveFixedComposition();
					responsiveFixedButton->SetAlignmentToParent(Margin(0, 0, 0, 0));
					responsiveFixedButton->AddChild(dropdownButton->GetBoundsComposition());

					dropdownMenu = new GuiRibbonGroupMenu(theme::ThemeName::Menu, dropdownButton);
				}

				responsiveView = new GuiResponsiveViewComposition();
				responsiveView->SetAlignmentToParent(Margin(0, 0, 0, 0));
				responsiveView->GetViews().Add(responsiveStack);

				containerComposition->AddChild(responsiveView);

				ExpandableChanged.SetAssociatedComposition(boundsComposition);
				ExpandButtonClicked.SetAssociatedComposition(boundsComposition);
				LargeImageChanged.SetAssociatedComposition(boundsComposition);

				TextChanged.AttachMethod(this, &GuiRibbonGroup::OnTextChanged);
				boundsComposition->BoundsChanged.AttachMethod(this, &GuiRibbonGroup::OnBoundsChanged);
				responsiveView->BeforeSwitchingView.AttachMethod(this, &GuiRibbonGroup::OnBeforeSwitchingView);
				dropdownButton->BeforeSubMenuOpening.AttachMethod(this, &GuiRibbonGroup::OnBeforeSubMenuOpening);
			}

			GuiRibbonGroup::~GuiRibbonGroup()
			{
				if (!responsiveView->GetViews().Contains(responsiveFixedButton))
				{
					SafeDeleteComposition(responsiveFixedButton);
				}
				delete dropdownMenu;
			}

			bool GuiRibbonGroup::GetExpandable()
			{
				return expandable;
			}

			void GuiRibbonGroup::SetExpandable(bool value)
			{
				if (expandable != value)
				{
					expandable = value;
					GetControlTemplateObject(true)->SetExpandable(expandable);
					ExpandableChanged.Execute(GetNotifyEventArguments());
				}
			}

			Ptr<GuiImageData> GuiRibbonGroup::GetLargeImage()
			{
				return largeImage;
			}

			void GuiRibbonGroup::SetLargeImage(Ptr<GuiImageData> value)
			{
				if (largeImage != value)
				{
					largeImage = value;
					dropdownButton->SetLargeImage(value);
					LargeImageChanged.Execute(GetNotifyEventArguments());

					if (value)
					{
						if (!responsiveView->GetViews().Contains(responsiveFixedButton))
						{
							responsiveView->GetViews().Add(responsiveFixedButton);
						}
					}
					else
					{
						if (responsiveView->GetViews().Contains(responsiveFixedButton))
						{
							responsiveView->GetViews().Remove(responsiveFixedButton);
						}
					}
				}
			}

			collections::ObservableListBase<GuiControl*>& GuiRibbonGroup::GetItems()
			{
				return items;
			}

/***********************************************************************
GuiRibbonButtonsItemCollection
***********************************************************************/

			bool GuiRibbonButtonsItemCollection::QueryInsert(vint index, GuiControl* const& value)
			{
				return !value->GetBoundsComposition()->GetParent();
			}

			void GuiRibbonButtonsItemCollection::AfterInsert(vint index, GuiControl* const& value)
			{
				buttons->responsiveView->GetSharedControls().Add(value);
				buttons->SetButtonThemeName(buttons->responsiveView->GetCurrentView(), value);

				for (vint i = 0; i < sizeof(buttons->views) / sizeof(*buttons->views); i++)
				{
					if (auto view = buttons->views[i])
					{
						auto stack = dynamic_cast<GuiStackComposition*>(view->Children()[0]);

						auto shared = new GuiResponsiveSharedComposition();
						shared->SetAlignmentToParent(Margin(0, 0, 0, 0));
						shared->SetShared(value);

						auto item = new GuiStackItemComposition();
						item->AddChild(shared);

						stack->InsertStackItem(index, item);
					}
				}
			}

			void GuiRibbonButtonsItemCollection::BeforeRemove(vint index, GuiControl* const& value)
			{
				CHECK_FAIL(L"GuiRibbonButtonsItemCollection::BeforeRemove(vint, GuiControl* const&)#Controls are not allowed to be removed from GuiRibbonButtons.");
			}

			GuiRibbonButtonsItemCollection::GuiRibbonButtonsItemCollection(GuiRibbonButtons* _buttons)
				:buttons(_buttons)
			{
			}

			GuiRibbonButtonsItemCollection::~GuiRibbonButtonsItemCollection()
			{
			}

/***********************************************************************
GuiRibbonButtons
***********************************************************************/

			void GuiRibbonButtons::BeforeControlTemplateUninstalled_()
			{
			}

			void GuiRibbonButtons::AfterControlTemplateInstalled_(bool initialize)
			{
				FOREACH(GuiControl*, button, buttons)
				{
					SetButtonThemeName(responsiveView->GetCurrentView(), button);
				}
			}

			void GuiRibbonButtons::OnBeforeSwitchingView(compositions::GuiGraphicsComposition* sender, compositions::GuiItemEventArgs& arguments)
			{
				FOREACH(GuiControl*, button, buttons)
				{
					SetButtonThemeName(responsiveView->GetViews()[arguments.itemIndex], button);
				}
			}

			void GuiRibbonButtons::SetButtonThemeName(compositions::GuiResponsiveCompositionBase* fixed, GuiControl* button)
			{
				if (fixed && button)
				{
					auto themeName = button->GetControlThemeName();
					vint type = -1;
					switch (themeName)
					{
					case ThemeName::RibbonLargeButton:
					case ThemeName::RibbonSmallButton:
					case ThemeName::ToolstripButton:
						type = 0;
						break;
					case ThemeName::RibbonLargeDropdownButton:
					case ThemeName::RibbonSmallDropdownButton:
					case ThemeName::ToolstripDropdownButton:
						type = 1;
						break;
					case ThemeName::RibbonLargeSplitButton:
					case ThemeName::RibbonSmallSplitButton:
					case ThemeName::ToolstripSplitButton:
						type = 2;
						break;
					}

					if (type != -1)
					{
						ThemeName themeName = ThemeName::Unknown;
						TemplateProperty<GuiToolstripButtonTemplate> controlTemplate;

						if (fixed == views[(vint)RibbonButtonSize::Large])
						{
							switch (type)
							{
							case 0: themeName = ThemeName::RibbonLargeButton; break;
							case 1: themeName = ThemeName::RibbonLargeDropdownButton; break;
							case 2: themeName = ThemeName::RibbonLargeSplitButton; break;
							}
						}
						else if (fixed == views[(vint)RibbonButtonSize::Small])
						{
							switch (type)
							{
							case 0: themeName = ThemeName::RibbonSmallButton; break;
							case 1: themeName = ThemeName::RibbonSmallDropdownButton; break;
							case 2: themeName = ThemeName::RibbonSmallSplitButton; break;
							}
						}
						else if (fixed == views[(vint)RibbonButtonSize::Icon])
						{
							switch (type)
							{
							case 0: themeName = ThemeName::ToolstripButton; break;
							case 1: themeName = ThemeName::ToolstripDropdownButton; break;
							case 2: themeName = ThemeName::ToolstripSplitButton; break;
							}
						}

						if (auto ct = GetControlTemplateObject(false))
						{
							if (fixed == views[(vint)RibbonButtonSize::Large])
							{
								switch (type)
								{
								case 0: controlTemplate = ct->GetLargeButtonTemplate(); break;
								case 1: controlTemplate = ct->GetLargeDropdownButtonTemplate(); break;
								case 2: controlTemplate = ct->GetLargeSplitButtonTemplate(); break;
								}
							}
							else if (fixed == views[(vint)RibbonButtonSize::Small])
							{
								switch (type)
								{
								case 0: controlTemplate = ct->GetSmallButtonTemplate(); break;
								case 1: controlTemplate = ct->GetSmallDropdownButtonTemplate(); break;
								case 2: controlTemplate = ct->GetSmallSplitButtonTemplate(); break;
								}
							}
							else if (fixed == views[(vint)RibbonButtonSize::Icon])
							{
								switch (type)
								{
								case 0: controlTemplate = ct->GetIconButtonTemplate(); break;
								case 1: controlTemplate = ct->GetIconDropdownButtonTemplate(); break;
								case 2: controlTemplate = ct->GetIconSplitButtonTemplate(); break;
								}
							}
						}

						button->SetControlThemeNameAndTemplate(themeName, controlTemplate);
					}
				}
			}

			GuiRibbonButtons::GuiRibbonButtons(theme::ThemeName themeName, RibbonButtonSize _maxSize, RibbonButtonSize _minSize)
				:GuiControl(themeName)
				, maxSize(_maxSize)
				, minSize(_minSize)
				, buttons(this)
			{
				responsiveView = new GuiResponsiveViewComposition();
				responsiveView->SetDirection(ResponsiveDirection::Horizontal);
				responsiveView->SetAlignmentToParent(Margin(0, 0, 0, 0));
				responsiveView->BeforeSwitchingView.AttachMethod(this, &GuiRibbonButtons::OnBeforeSwitchingView);

				auto installButton = [&](GuiTableComposition* table, vint row, vint column, GuiControl* buttonContainer)
				{
					auto shared = new GuiResponsiveSharedComposition();
					shared->SetAlignmentToParent(Margin(0, 0, 0, 0));
					shared->SetShared(buttonContainer);

					auto cell = new GuiCellComposition();
					cell->SetSite(row, column, 1, 1);
					cell->AddChild(shared);

					table->AddChild(cell);
				};

				for (vint i = 0; i < sizeof(views) / sizeof(*views); i++)
				{
					if ((vint)maxSize <= i && i <= (vint)minSize)
					{
						auto stack = new GuiStackComposition();
						stack->SetAlignmentToParent(Margin(0, 0, 0, 0));
						stack->SetMinSizeLimitation(GuiGraphicsComposition::LimitToElementAndChildren);
						stack->SetDirection(i == 0 ? GuiStackComposition::Horizontal : GuiStackComposition::Vertical);

						views[i] = new GuiResponsiveFixedComposition();
						views[i]->AddChild(stack);
						responsiveView->GetViews().Add(views[i]);
					}
				}

				containerComposition->AddChild(responsiveView);
			}

			GuiRibbonButtons::~GuiRibbonButtons()
			{
			}

			collections::ObservableListBase<GuiControl*>& GuiRibbonButtons::GetButtons()
			{
				return buttons;
			}

/***********************************************************************
GuiRibbonToolstripsGroupCollection
***********************************************************************/

			bool GuiRibbonToolstripsGroupCollection::QueryInsert(vint index, GuiToolstripGroup* const& value)
			{
				return !value->GetBoundsComposition()->GetParent();
			}

			void GuiRibbonToolstripsGroupCollection::AfterInsert(vint index, GuiToolstripGroup* const& value)
			{
				toolstrips->RearrangeToolstripGroups();
			}

			void GuiRibbonToolstripsGroupCollection::AfterRemove(vint index, vint count)
			{
				toolstrips->RearrangeToolstripGroups();
			}

			GuiRibbonToolstripsGroupCollection::GuiRibbonToolstripsGroupCollection(GuiRibbonToolstrips* _toolstrips)
				:toolstrips(_toolstrips)
			{
			}

			GuiRibbonToolstripsGroupCollection::~GuiRibbonToolstripsGroupCollection()
			{
			}

/***********************************************************************
GuiRibbonToolstrips
***********************************************************************/

#define ARRLEN(X) sizeof(X) / sizeof(*X)

			void GuiRibbonToolstrips::BeforeControlTemplateUninstalled_()
			{
			}

			void GuiRibbonToolstrips::AfterControlTemplateInstalled_(bool initialize)
			{
				auto ct = GetControlTemplateObject(true);
				for (vint i = 0; i < ARRLEN(toolbars); i++)
				{
					toolbars[i]->SetControlTemplate(ct->GetToolbarTemplate());
				}
			}

			void GuiRibbonToolstrips::OnBeforeSwitchingView(compositions::GuiGraphicsComposition* sender, compositions::GuiItemEventArgs& arguments)
			{
				RearrangeToolstripGroups(arguments.itemIndex);
			}

			void GuiRibbonToolstrips::RearrangeToolstripGroups(vint viewIndex)
			{
				static_assert(ARRLEN(longContainers) == 2, "");
				static_assert(ARRLEN(shortContainers) == 3, "");

				if (viewIndex == -1)
				{
					viewIndex = responsiveView->GetViews().IndexOf(responsiveView->GetCurrentView());
				}

				for (vint i = 0; i < ARRLEN(longContainers); i++)
				{
					longContainers[i]->GetToolstripItems().Clear();
				}
				for (vint i = 0; i < ARRLEN(shortContainers); i++)
				{
					shortContainers[i]->GetToolstripItems().Clear();
				}

				vint count = viewIndex == 0 ? 2 : 3;

				if (groups.Count() <= count)
				{
					auto containers = viewIndex == 0 ? longContainers : shortContainers;
					for (vint i = 0; i < groups.Count(); i++)
					{
						containers[i]->GetToolstripItems().Add(groups[i]);
					}
				}
				else if (count == 3)
				{
#define DELTA(POSTFIX) (abs(count1##POSTFIX - count2##POSTFIX) + abs(count2##POSTFIX - count3##POSTFIX) + abs(count3##POSTFIX - count1##POSTFIX))
#define DEFINE_COUNT(POSTFIX, OFFSET_FIRST, OFFSET_LAST) \
					vint count1##POSTFIX = count1_o + (OFFSET_FIRST); \
					vint count2##POSTFIX = count2_o - (OFFSET_FIRST) - (OFFSET_LAST); \
					vint count3##POSTFIX = count3_o + (OFFSET_LAST)
#define MIN(a, b) (a)<(b)?(a):(b)

					vint firstGroupCount = 0;
					vint lastGroupCount = 0;

					vint count1_o = 0;
					vint count2_o = From(groups)
						.Select([](GuiToolstripGroup* group) {return group->GetToolstripItems().Count(); })
						.Aggregate([](vint a, vint b) {return a + b; });
					vint count3_o = 0;
					vint delta_o = DELTA(_o);

					while (firstGroupCount + lastGroupCount < groups.Count())
					{
						auto newFirstGroup = groups[firstGroupCount];
						auto newLastGroup = groups[groups.Count() - lastGroupCount - 1];

						DEFINE_COUNT(_f, newFirstGroup->GetToolstripItems().Count(), 0);
						vint delta_f = DELTA(_f);

						DEFINE_COUNT(_l, 0, newLastGroup->GetToolstripItems().Count());
						vint delta_l = DELTA(_l);

						vint delta = MIN(delta_o, MIN(delta_f, delta_l));
						if (delta == delta_f)
						{
							firstGroupCount++;
							count1_o = count1_f;
							count2_o = count2_f;
							count3_o = count3_f;
							delta_o = delta_f;
						}
						else if (delta == delta_l)
						{
							lastGroupCount++;
							count1_o = count1_l;
							count2_o = count2_l;
							count3_o = count3_l;
							delta_o = delta_l;
						}
						else
						{
							break;
						}
					}

					vint minMiddle = firstGroupCount;
					vint maxMiddle = groups.Count() - lastGroupCount - 1;
					for (vint j = 0; j < groups.Count(); j++)
					{
						shortContainers[
							j < minMiddle ? 0 :
							j>maxMiddle ? 2 :
							1
						]->GetToolstripItems().Add(groups[j]);
					}

#undef MIN
#undef DEFINE_COUNT
#undef DELTA
				}
				else if (count == 2)
				{
					vint firstGroupCount = groups.Count();
					{
						vint count1 = 0;
						vint count2 = From(groups)
							.Select([](GuiToolstripGroup* group) {return group->GetToolstripItems().Count(); })
							.Aggregate([](vint a, vint b) {return a + b; });
						vint delta = abs(count2 - count1);

						for (vint i = 0; i < groups.Count(); i++)
						{
							auto groupCount = groups[i]->GetToolstripItems().Count();
							vint count1_2 = count1 + groupCount;
							vint count2_2 = count2 - groupCount;
							vint delta_2 = abs(count2_2 - count1_2);

							if (delta < delta_2)
							{
								firstGroupCount = i;
								break;
							}

							count1 = count1_2;
							count2 = count2_2;
							delta = delta_2;
						}
					}

					for (vint j = 0; j < groups.Count(); j++)
					{
						longContainers[j < firstGroupCount ? 0 : 1]->GetToolstripItems().Add(groups[j]);
					}
				}
			}

			GuiRibbonToolstrips::GuiRibbonToolstrips(theme::ThemeName themeName)
				:GuiControl(themeName)
				, groups(this)
			{
				responsiveView = new GuiResponsiveViewComposition();
				responsiveView->SetDirection(ResponsiveDirection::Horizontal);
				responsiveView->SetAlignmentToParent(Margin(0, 0, 0, 0));
				responsiveView->BeforeSwitchingView.AttachMethod(this, &GuiRibbonToolstrips::OnBeforeSwitchingView);

				vint toolbarIndex = 0;
				for (vint i = 0; i < sizeof(views) / sizeof(*views); i++)
				{
					auto containers = i == 0 ? longContainers : shortContainers;
					vint count = i == 0 ? 2 : 3;

					auto table = new GuiTableComposition();
					table->SetAlignmentToParent(Margin(0, 0, 0, 0));
					table->SetMinSizeLimitation(GuiGraphicsComposition::LimitToElementAndChildren);
					
					table->SetRowsAndColumns(count * 2 + 1, 1);
					table->SetColumnOption(0, GuiCellOption::MinSizeOption());
					table->SetRowOption(0, GuiCellOption::PercentageOption(1.0));
					for (vint j = 0; j < count; j++)
					{
						table->SetRowOption(j * 2 + 1, GuiCellOption::MinSizeOption());
						table->SetRowOption(j * 2 + 2, GuiCellOption::PercentageOption(1.0));
					}

					for (vint j = 0; j < count; j++)
					{
						auto toolbar = new GuiToolstripToolBar(theme::ThemeName::ToolstripToolBar);
						toolbar->GetBoundsComposition()->SetAlignmentToParent(Margin(0, 0, 0, 0));
						toolbars[toolbarIndex++] = toolbar;

						auto cell = new GuiCellComposition();
						cell->SetSite(j * 2 + 1, 0, 1, 1);
						cell->AddChild(toolbar->GetBoundsComposition());
						table->AddChild(cell);

						auto container = new GuiToolstripGroupContainer(theme::ThemeName::CustomControl);
						toolbar->GetToolstripItems().Add(container);
						containers[j] = container;
					}

					views[i] = new GuiResponsiveFixedComposition();
					views[i]->AddChild(table);
					responsiveView->GetViews().Add(views[i]);
				}

				containerComposition->AddChild(responsiveView);
			}

			GuiRibbonToolstrips::~GuiRibbonToolstrips()
			{
			}

			collections::ObservableListBase<GuiToolstripGroup*>& GuiRibbonToolstrips::GetGroups()
			{
				return groups;
			}

#undef ARRLEN

/***********************************************************************
GuiRibbonGallery::CommandExecutor
***********************************************************************/

			GuiRibbonGallery::CommandExecutor::CommandExecutor(GuiRibbonGallery* _gallery)
				:gallery(_gallery)
			{
			}

			GuiRibbonGallery::CommandExecutor::~CommandExecutor()
			{
			}

			void GuiRibbonGallery::CommandExecutor::NotifyScrollUp()
			{
				gallery->RequestedScrollUp.Execute(gallery->GetNotifyEventArguments());
			}

			void GuiRibbonGallery::CommandExecutor::NotifyScrollDown()
			{
				gallery->RequestedScrollDown.Execute(gallery->GetNotifyEventArguments());
			}

			void GuiRibbonGallery::CommandExecutor::NotifyDropdown()
			{
				gallery->RequestedDropdown.Execute(gallery->GetNotifyEventArguments());
			}

/***********************************************************************
GuiRibbonGallery
***********************************************************************/

			void GuiRibbonGallery::BeforeControlTemplateUninstalled_()
			{
				auto ct = GetControlTemplateObject(false);
				if (!ct) return;

				ct->SetCommands(nullptr);
			}

			void GuiRibbonGallery::AfterControlTemplateInstalled_(bool initialize)
			{
				auto ct = GetControlTemplateObject(true);
				ct->SetCommands(commandExecutor.Obj());
				ct->SetScrollUpEnabled(scrollUpEnabled);
				ct->SetScrollDownEnabled(scrollDownEnabled);
			}

			GuiRibbonGallery::GuiRibbonGallery(theme::ThemeName themeName)
				:GuiControl(themeName)
			{
				commandExecutor = new CommandExecutor(this);

				ScrollUpEnabledChanged.SetAssociatedComposition(boundsComposition);
				ScrollDownEnabledChanged.SetAssociatedComposition(boundsComposition);
				RequestedScrollUp.SetAssociatedComposition(boundsComposition);
				RequestedScrollDown.SetAssociatedComposition(boundsComposition);
				RequestedDropdown.SetAssociatedComposition(boundsComposition);
			}

			GuiRibbonGallery::~GuiRibbonGallery()
			{
			}

			bool GuiRibbonGallery::GetScrollUpEnabled()
			{
				return scrollUpEnabled;
			}

			void GuiRibbonGallery::SetScrollUpEnabled(bool value)
			{
				if (scrollUpEnabled != value)
				{
					scrollUpEnabled = value;
					GetControlTemplateObject(true)->SetScrollUpEnabled(value);
				}
			}

			bool GuiRibbonGallery::GetScrollDownEnabled()
			{
				return scrollDownEnabled;
			}

			void GuiRibbonGallery::SetScrollDownEnabled(bool value)
			{
				if (scrollDownEnabled != value)
				{
					scrollDownEnabled = value;
					GetControlTemplateObject(true)->SetScrollDownEnabled(value);
				}
			}

/***********************************************************************
GuiRibbonToolstripMenu
***********************************************************************/

			void GuiRibbonToolstripMenu::BeforeControlTemplateUninstalled_()
			{
				auto ct = GetControlTemplateObject(false);
				if (!ct) return;

				if (auto cc = ct->GetContentComposition())
				{
					cc->RemoveChild(contentComposition);
				}
			}

			void GuiRibbonToolstripMenu::AfterControlTemplateInstalled_(bool initialize)
			{
				auto ct = GetControlTemplateObject(true);
				if (auto cc = ct->GetContentComposition())
				{
					cc->AddChild(contentComposition);
				}
			}

			GuiRibbonToolstripMenu::GuiRibbonToolstripMenu(theme::ThemeName themeName, GuiControl* owner)
				:GuiToolstripMenu(themeName, owner)
			{
				contentComposition = new GuiBoundsComposition();
				contentComposition->SetAlignmentToParent(Margin(0, 0, 0, 0));
				contentComposition->SetMinSizeLimitation(GuiGraphicsComposition::LimitToElementAndChildren);
			}

			GuiRibbonToolstripMenu::~GuiRibbonToolstripMenu()
			{
				if (!contentComposition->GetParent())
				{
					SafeDeleteComposition(contentComposition);
				}
			}

			compositions::GuiGraphicsComposition* GuiRibbonToolstripMenu::GetContentComposition()
			{
				return contentComposition;
			}
		}
	}
}

/***********************************************************************
.\CONTROLS\TOOLSTRIPPACKAGE\GUITOOLSTRIPMENU.CPP
***********************************************************************/

namespace vl
{
	namespace presentation
	{
		namespace controls
		{
			using namespace collections;
			using namespace compositions;

/***********************************************************************
GuiToolstripCollectionBase
***********************************************************************/

			const wchar_t* const IToolstripUpdateLayoutInvoker::Identifier = L"vl::presentation::controls::IToolstripUpdateLayoutInvoker";

			void GuiToolstripCollectionBase::InvokeUpdateLayout()
			{
				if(contentCallback)
				{
					contentCallback->UpdateLayout();
				}
			}

			bool GuiToolstripCollectionBase::QueryInsert(vint index, GuiControl* const& child)
			{
				return !items.Contains(child) && !child->GetBoundsComposition()->GetParent();
			}

			void GuiToolstripCollectionBase::BeforeRemove(vint index, GuiControl* const& child)
			{
				if (auto invoker = child->QueryTypedService<IToolstripUpdateLayoutInvoker>())
				{
					invoker->SetCallback(nullptr);
				}
				InvokeUpdateLayout();
			}

			void GuiToolstripCollectionBase::AfterInsert(vint index, GuiControl* const& child)
			{
				if (auto invoker = child->QueryTypedService<IToolstripUpdateLayoutInvoker>())
				{
					invoker->SetCallback(contentCallback);
				}
				InvokeUpdateLayout();
			}

			void GuiToolstripCollectionBase::AfterRemove(vint index, vint count)
			{
				InvokeUpdateLayout();
			}

			GuiToolstripCollectionBase::GuiToolstripCollectionBase(IToolstripUpdateLayout* _contentCallback)
				:contentCallback(_contentCallback)
			{
			}

			GuiToolstripCollectionBase::~GuiToolstripCollectionBase()
			{
			}

/***********************************************************************
GuiToolstripCollection
***********************************************************************/

			void GuiToolstripCollection::UpdateItemVisibility(vint index, GuiControl* child)
			{
				auto stackItem = stackComposition->GetStackItems()[index];
				if (child->GetVisible())
				{
					stackItem->SetMinSizeLimitation(GuiGraphicsComposition::LimitToElementAndChildren);
					child->GetBoundsComposition()->SetAlignmentToParent(Margin(0, 0, 0, 0));
				}
				else
				{
					stackItem->SetMinSizeLimitation(GuiGraphicsComposition::NoLimit);
					child->GetBoundsComposition()->SetAlignmentToParent(Margin(-1, -1, -1, -1));
				}
			}

			void GuiToolstripCollection::OnItemVisibleChanged(compositions::GuiGraphicsComposition* sender, compositions::GuiEventArgs& arguments)
			{
				auto child = sender->GetRelatedControl();
				vint index = IndexOf(child);
				UpdateItemVisibility(index, child);
				InvokeUpdateLayout();
			}

			void GuiToolstripCollection::BeforeRemove(vint index, GuiControl* const& child)
			{
				GuiStackItemComposition* stackItem = stackComposition->GetStackItems().Get(index);

				auto eventHandler = eventHandlers[index];
				child->VisibleChanged.Detach(eventHandler);
				eventHandlers.RemoveAt(index);

				GuiToolstripCollectionBase::BeforeRemove(index, child);
			}

			void GuiToolstripCollection::AfterInsert(vint index, GuiControl* const& child)
			{
				{
					GuiStackItemComposition* stackItem = new GuiStackItemComposition;
					stackItem->AddChild(child->GetBoundsComposition());
					stackComposition->InsertStackItem(index, stackItem);
				}
				{
					auto eventHandler = child->VisibleChanged.AttachMethod(this, &GuiToolstripCollection::OnItemVisibleChanged);
					eventHandlers.Insert(index, eventHandler);
				}
				UpdateItemVisibility(index, child);
				GuiToolstripCollectionBase::AfterInsert(index, child);
			}

			void GuiToolstripCollection::AfterRemove(vint index, vint count)
			{
				for (vint i = 0; i < count; i++)
				{
					auto stackItem = stackComposition->GetStackItems().Get(index);
					stackComposition->RemoveChild(stackItem);
					SafeDeleteComposition(stackItem);
				}
				GuiToolstripCollectionBase::AfterRemove(index, count);
			}

			GuiToolstripCollection::GuiToolstripCollection(IToolstripUpdateLayout* _contentCallback, compositions::GuiStackComposition* _stackComposition)
				:GuiToolstripCollectionBase(_contentCallback)
				,stackComposition(_stackComposition)
			{
			}

			GuiToolstripCollection::~GuiToolstripCollection()
			{
			}

/***********************************************************************
GuiToolstripMenu
***********************************************************************/

			void GuiToolstripMenu::UpdateLayout()
			{
				sharedSizeRootComposition->ForceCalculateSizeImmediately();
			}

			GuiToolstripMenu::GuiToolstripMenu(theme::ThemeName themeName, GuiControl* _owner)
				:GuiMenu(themeName, _owner)
			{
				sharedSizeRootComposition = new GuiSharedSizeRootComposition();
				sharedSizeRootComposition->SetAlignmentToParent(Margin(0, 0, 0, 0));
				sharedSizeRootComposition->SetMinSizeLimitation(GuiGraphicsComposition::LimitToElementAndChildren);
				containerComposition->AddChild(sharedSizeRootComposition);

				stackComposition=new GuiStackComposition;
				stackComposition->SetDirection(GuiStackComposition::Vertical);
				stackComposition->SetAlignmentToParent(Margin(0, 0, 0, 0));
				stackComposition->SetMinSizeLimitation(GuiGraphicsComposition::LimitToElementAndChildren);
				sharedSizeRootComposition->AddChild(stackComposition);
				
				toolstripItems = new GuiToolstripCollection(this, stackComposition);
			}

			GuiToolstripMenu::~GuiToolstripMenu()
			{
			}

			collections::ObservableListBase<GuiControl*>& GuiToolstripMenu::GetToolstripItems()
			{
				return *toolstripItems.Obj();
			}

/***********************************************************************
GuiToolstripMenuBar
***********************************************************************/
			
			GuiToolstripMenuBar::GuiToolstripMenuBar(theme::ThemeName themeName)
				:GuiMenuBar(themeName)
			{
				stackComposition=new GuiStackComposition;
				stackComposition->SetDirection(GuiStackComposition::Horizontal);
				stackComposition->SetAlignmentToParent(Margin(0, 0, 0, 0));
				stackComposition->SetMinSizeLimitation(GuiGraphicsComposition::LimitToElementAndChildren);
				containerComposition->AddChild(stackComposition);

				toolstripItems=new GuiToolstripCollection(nullptr, stackComposition);
			}

			GuiToolstripMenuBar::~GuiToolstripMenuBar()
			{
			}

			collections::ObservableListBase<GuiControl*>& GuiToolstripMenuBar::GetToolstripItems()
			{
				return *toolstripItems.Obj();
			}

/***********************************************************************
GuiToolstripToolBar
***********************************************************************/
				
			GuiToolstripToolBar::GuiToolstripToolBar(theme::ThemeName themeName)
				:GuiControl(themeName)
			{
				stackComposition=new GuiStackComposition;
				stackComposition->SetDirection(GuiStackComposition::Horizontal);
				stackComposition->SetAlignmentToParent(Margin(0, 0, 0, 0));
				stackComposition->SetMinSizeLimitation(GuiGraphicsComposition::LimitToElementAndChildren);
				containerComposition->AddChild(stackComposition);

				toolstripItems=new GuiToolstripCollection(nullptr, stackComposition);
			}

			GuiToolstripToolBar::~GuiToolstripToolBar()
			{
			}

			collections::ObservableListBase<GuiControl*>& GuiToolstripToolBar::GetToolstripItems()
			{
				return *toolstripItems.Obj();
			}

/***********************************************************************
GuiToolstripButton
***********************************************************************/

			void GuiToolstripButton::SetCallback(IToolstripUpdateLayout* _callback)
			{
				callback = _callback;
			}

			void GuiToolstripButton::UpdateCommandContent()
			{
				if(command)
				{
					SetLargeImage(command->GetLargeImage());
					SetImage(command->GetImage());
					SetText(command->GetText());
					SetEnabled(command->GetEnabled());
					SetSelected(command->GetSelected());
					if(command->GetShortcut())
					{
						SetShortcutText(command->GetShortcut()->GetName());
					}
					else
					{
						SetShortcutText(L"");
					}
				}
				else
				{
					SetLargeImage(nullptr);
					SetImage(nullptr);
					SetText(L"");
					SetEnabled(true);
					SetSelected(false);
					SetShortcutText(L"");
				}
			}

			void GuiToolstripButton::OnLayoutAwaredPropertyChanged(compositions::GuiGraphicsComposition* sender, compositions::GuiEventArgs& arguments)
			{
				if (callback)
				{
					callback->UpdateLayout();
				}
			}

			void GuiToolstripButton::OnClicked(compositions::GuiGraphicsComposition* sender, compositions::GuiEventArgs& arguments)
			{
				if(command)
				{
					command->Executed.ExecuteWithNewSender(arguments, sender);
				}
			}

			void GuiToolstripButton::OnCommandDescriptionChanged(compositions::GuiGraphicsComposition* sender, compositions::GuiEventArgs& arguments)
			{
				UpdateCommandContent();
			}

			GuiToolstripButton::GuiToolstripButton(theme::ThemeName themeName)
				:GuiMenuButton(themeName)
				,command(0)
			{
				Clicked.AttachMethod(this, &GuiToolstripButton::OnClicked);
				TextChanged.AttachMethod(this, &GuiToolstripButton::OnLayoutAwaredPropertyChanged);
				ShortcutTextChanged.AttachMethod(this, &GuiToolstripButton::OnLayoutAwaredPropertyChanged);
			}

			GuiToolstripButton::~GuiToolstripButton()
			{
			}

			GuiToolstripCommand* GuiToolstripButton::GetCommand()
			{
				return command;
			}

			void GuiToolstripButton::SetCommand(GuiToolstripCommand* value)
			{
				if(command!=value)
				{
					if(command)
					{
						command->DescriptionChanged.Detach(descriptionChangedHandler);
					}
					command=0;
					descriptionChangedHandler=0;
					if(value)
					{
						command=value;
						descriptionChangedHandler=command->DescriptionChanged.AttachMethod(this, &GuiToolstripButton::OnCommandDescriptionChanged);
					}
					UpdateCommandContent();
				}
			}

			GuiToolstripMenu* GuiToolstripButton::GetToolstripSubMenu()
			{
				return dynamic_cast<GuiToolstripMenu*>(GetSubMenu());
			}

			GuiToolstripMenu* GuiToolstripButton::EnsureToolstripSubMenu()
			{
				if (!GetSubMenu())
				{
					CreateToolstripSubMenu({});
				}
				return dynamic_cast<GuiToolstripMenu*>(GetSubMenu());
			}

			void GuiToolstripButton::CreateToolstripSubMenu(TemplateProperty<templates::GuiMenuTemplate> subMenuTemplate)
			{
				if (!subMenu)
				{
					auto newSubMenu = new GuiToolstripMenu(theme::ThemeName::Menu, this);
					if (subMenuTemplate)
					{
						newSubMenu->SetControlTemplate(subMenuTemplate);
					}
					else
					{
						newSubMenu->SetControlTemplate(GetControlTemplateObject(true)->GetSubMenuTemplate());
					}
					SetSubMenu(newSubMenu, true);
				}
			}

			IDescriptable* GuiToolstripButton::QueryService(const WString& identifier)
			{
				if (identifier == IToolstripUpdateLayoutInvoker::Identifier)
				{
					return (IToolstripUpdateLayoutInvoker*)this;
				}
				else
				{
					return GuiMenuButton::QueryService(identifier);
				}
			}

/***********************************************************************
GuiToolstripNestedContainer
***********************************************************************/

			void GuiToolstripNestedContainer::UpdateLayout()
			{
				if (callback)
				{
					callback->UpdateLayout();
				}
			}

			void GuiToolstripNestedContainer::SetCallback(IToolstripUpdateLayout* _callback)
			{
				callback = _callback;
			}

			GuiToolstripNestedContainer::GuiToolstripNestedContainer(theme::ThemeName themeName)
				:GuiControl(themeName)
			{
			}

			GuiToolstripNestedContainer::~GuiToolstripNestedContainer()
			{
			}

			IDescriptable* GuiToolstripNestedContainer::QueryService(const WString& identifier)
			{
				if (identifier == IToolstripUpdateLayoutInvoker::Identifier)
				{
					return (IToolstripUpdateLayoutInvoker*)this;
				}
				else
				{
					return GuiControl::QueryService(identifier);
				}
			}

/***********************************************************************
GuiToolstripGroupContainer::GroupCollection
***********************************************************************/

			void GuiToolstripGroupContainer::GroupCollection::BeforeRemove(vint index, GuiControl* const& child)
			{
				auto controlStackItem = container->stackComposition->GetStackItems()[index * 2];
				CHECK_ERROR(controlStackItem->RemoveChild(child->GetBoundsComposition()), L"GuiToolstripGroupContainer::GroupCollection::BeforeRemove(vint, GuiControl* const&)#Internal error");
			}

			void GuiToolstripGroupContainer::GroupCollection::AfterInsert(vint index, GuiControl* const& child)
			{
				auto controlStackItem = new GuiStackItemComposition;
				child->GetBoundsComposition()->SetAlignmentToParent(Margin(0, 0, 0, 0));
				CHECK_ERROR(controlStackItem->AddChild(child->GetBoundsComposition()), L"GuiToolstripGroupContainer::GroupCollection::AfterInsert(vint, GuiControl* const&)#Internal error");

				if (container->stackComposition->GetStackItems().Count() > 0)
				{
					auto splitterStackItem = new GuiStackItemComposition;
					auto splitter = new GuiControl(container->splitterThemeName);
					if (splitterTemplate)
					{
						splitter->SetControlTemplate(splitterTemplate);
					}
					splitter->GetBoundsComposition()->SetAlignmentToParent(Margin(0, 0, 0, 0));
					splitterStackItem->AddChild(splitter->GetBoundsComposition());
					container->stackComposition->InsertStackItem(index == 0 ? 0 : index * 2 - 1, splitterStackItem);
				}

				container->stackComposition->InsertStackItem(index * 2, controlStackItem);

				GuiToolstripCollectionBase::AfterInsert(index, child);
			}

			void GuiToolstripGroupContainer::GroupCollection::AfterRemove(vint index, vint count)
			{
				vint min = index * 2;
				vint max = (index + count - 1) * 2;
				for (vint i = min; i <= max; i++)
				{
					auto stackItem = container->stackComposition->GetStackItems()[min];
					container->stackComposition->RemoveChild(stackItem);
					SafeDeleteComposition(stackItem);
				}
				GuiToolstripCollectionBase::AfterRemove(index, count);
			}

			GuiToolstripGroupContainer::GroupCollection::GroupCollection(GuiToolstripGroupContainer* _container)
				:GuiToolstripCollectionBase(_container)
				, container(_container)
			{
			}

			GuiToolstripGroupContainer::GroupCollection::~GroupCollection()
			{
			}

			GuiToolstripGroupContainer::ControlTemplatePropertyType GuiToolstripGroupContainer::GroupCollection::GetSplitterTemplate()
			{
				return splitterTemplate;
			}

			void GuiToolstripGroupContainer::GroupCollection::SetSplitterTemplate(const ControlTemplatePropertyType& value)
			{
				splitterTemplate = value;
				RebuildSplitters();
			}

			void GuiToolstripGroupContainer::GroupCollection::RebuildSplitters()
			{
				auto stack = container->stackComposition;
				vint count = stack->GetStackItems().Count();
				for (vint i = 1; i < count; i += 2)
				{
					auto stackItem = stack->GetStackItems()[i];
					{
						auto control = stackItem->Children()[0]->GetAssociatedControl();
						CHECK_ERROR(control != nullptr, L"GuiToolstripGroupContainer::GroupCollection::RebuildSplitters()#Internal error");
						stackItem->RemoveChild(control->GetBoundsComposition());
						delete control;
					}
					{
						auto control = new GuiControl(container->splitterThemeName);
						if (splitterTemplate)
						{
							control->SetControlTemplate(splitterTemplate);
						}
						control->GetBoundsComposition()->SetAlignmentToParent(Margin(0, 0, 0, 0));
						stackItem->AddChild(control->GetBoundsComposition());
					}
				}
			}

/***********************************************************************
GuiToolstripGroupContainer
***********************************************************************/

			void GuiToolstripGroupContainer::OnParentLineChanged()
			{
				auto direction = GuiStackComposition::Horizontal;
				if (auto service = QueryTypedService<IGuiMenuService>())
				{
					if (service->GetPreferredDirection() == IGuiMenuService::Vertical)
					{
						direction = GuiStackComposition::Vertical;
					}
				}

				if (direction != stackComposition->GetDirection())
				{
					if (direction == GuiStackComposition::Vertical)
					{
						splitterThemeName = theme::ThemeName::MenuSplitter;
					}
					else
					{
						splitterThemeName = theme::ThemeName::ToolstripSplitter;
					}

					stackComposition->SetDirection(direction);
					splitterThemeName = splitterThemeName;
					groupCollection->RebuildSplitters();
					UpdateLayout();
				}

				GuiControl::OnParentLineChanged();
			}

			GuiToolstripGroupContainer::GuiToolstripGroupContainer(theme::ThemeName themeName)
				:GuiToolstripNestedContainer(themeName)
				, splitterThemeName(theme::ThemeName::ToolstripSplitter)
			{
				stackComposition = new GuiStackComposition;
				stackComposition->SetDirection(GuiStackComposition::Horizontal);
				stackComposition->SetAlignmentToParent(Margin(0, 0, 0, 0));
				stackComposition->SetMinSizeLimitation(GuiGraphicsComposition::LimitToElementAndChildren);
				containerComposition->AddChild(stackComposition);

				groupCollection = new GroupCollection(this);
			}

			GuiToolstripGroupContainer::~GuiToolstripGroupContainer()
			{
			}

			GuiToolstripGroupContainer::ControlTemplatePropertyType GuiToolstripGroupContainer::GetSplitterTemplate()
			{
				return groupCollection->GetSplitterTemplate();
			}

			void GuiToolstripGroupContainer::SetSplitterTemplate(const ControlTemplatePropertyType& value)
			{
				groupCollection->SetSplitterTemplate(value);
			}

			collections::ObservableListBase<GuiControl*>& GuiToolstripGroupContainer::GetToolstripItems()
			{
				return *groupCollection.Obj();
			}

/***********************************************************************
GuiToolstripGroup
***********************************************************************/

			void GuiToolstripGroup::OnParentLineChanged()
			{
				auto direction = GuiStackComposition::Horizontal;
				if (auto service = QueryTypedService<IGuiMenuService>())
				{
					if (service->GetPreferredDirection() == IGuiMenuService::Vertical)
					{
						direction = GuiStackComposition::Vertical;
					}
				}

				if (direction != stackComposition->GetDirection())
				{
					stackComposition->SetDirection(direction);
					UpdateLayout();
				}

				GuiControl::OnParentLineChanged();
			}

			GuiToolstripGroup::GuiToolstripGroup(theme::ThemeName themeName)
				:GuiToolstripNestedContainer(themeName)
			{
				stackComposition = new GuiStackComposition;
				stackComposition->SetDirection(GuiStackComposition::Horizontal);
				stackComposition->SetAlignmentToParent(Margin(0, 0, 0, 0));
				stackComposition->SetMinSizeLimitation(GuiGraphicsComposition::LimitToElementAndChildren);
				containerComposition->AddChild(stackComposition);

				toolstripItems = new GuiToolstripCollection(nullptr, stackComposition);
			}

			GuiToolstripGroup::~GuiToolstripGroup()
			{
			}

			collections::ObservableListBase<GuiControl*>& GuiToolstripGroup::GetToolstripItems()
			{
				return *toolstripItems.Obj();
			}
		}
	}
}

/***********************************************************************
.\CONTROLS\TOOLSTRIPPACKAGE\GUIRIBBONGALLERYLIST.CPP
***********************************************************************/


namespace vl
{
	namespace presentation
	{
		namespace controls
		{
			using namespace reflection::description;
			using namespace collections;
			using namespace compositions;
			using namespace templates;

			namespace list
			{

/***********************************************************************
list::GalleryGroup
***********************************************************************/

				GalleryGroup::GalleryGroup()
				{
				}

				GalleryGroup::~GalleryGroup()
				{
					if (eventHandler)
					{
						itemValues.Cast<IValueObservableList>()->ItemChanged.Remove(eventHandler);
					}
				}

				WString GalleryGroup::GetName()
				{
					return name;
				}

				Ptr<IValueList> GalleryGroup::GetItemValues()
				{
					return itemValues;
				}

/***********************************************************************
list::GroupedDataSource
***********************************************************************/

				void GroupedDataSource::RebuildItemSource()
				{
					ignoreGroupChanged = true;
					joinedItemSource.Clear();
					groupedItemSource.Clear();
					cachedGroupItemCounts.Clear();
					ignoreGroupChanged = false;

					if (itemSource)
					{
						if (GetGroupEnabled())
						{
							FOREACH_INDEXER(Value, groupValue, index, GetLazyList<Value>(itemSource))
							{
								auto group = MakePtr<GalleryGroup>();
								group->name = titleProperty(groupValue);
								group->itemValues = GetChildren(childrenProperty(groupValue));
								AttachGroupChanged(group, index);
								groupedItemSource.Add(group);
							}
						}
						else
						{
							auto group = MakePtr<GalleryGroup>();
							group->itemValues = GetChildren(itemSource);
							AttachGroupChanged(group, 0);
							groupedItemSource.Add(group);
						}
					}
				}

				Ptr<IValueList> GroupedDataSource::GetChildren(Ptr<IValueEnumerable> children)
				{
					if (!children)
					{
						return nullptr;
					}
					else if (auto list = children.Cast<IValueList>())
					{
						return list;
					}
					else
					{
						return IValueList::Create(GetLazyList<Value>(children));
					}
				}

				void GroupedDataSource::AttachGroupChanged(Ptr<GalleryGroup> group, vint index)
				{
					if (auto observable = group->itemValues.Cast<IValueObservableList>())
					{
						group->eventHandler = observable->ItemChanged.Add([this, index](vint start, vint oldCount, vint newCount)
						{
							OnGroupItemChanged(index, start, oldCount, newCount);
						});
					}
				}

				void GroupedDataSource::OnGroupChanged(vint start, vint oldCount, vint newCount)
				{
					if (!ignoreGroupChanged)
					{
						for (vint i = 0; i < oldCount; i++)
						{
							RemoveGroupFromJoined(start);
						}
						for (vint i = 0; i < newCount; i++)
						{
							InsertGroupToJoined(start + i);
						}
					}
				}

				void GroupedDataSource::OnGroupItemChanged(vint index, vint start, vint oldCount, vint newCount)
				{
					if (!ignoreGroupChanged)
					{
						vint countBeforeGroup = GetCountBeforeGroup(index);
						vint joinedIndex = countBeforeGroup + start;
						vint minCount = oldCount < newCount ? oldCount : newCount;
						auto itemValues = groupedItemSource[index]->itemValues;

						for (vint i = 0; i < minCount; i++)
						{
							joinedItemSource.Set(joinedIndex + i, itemValues->Get(start + i));
						}

						if (oldCount < newCount)
						{
							for (vint i = minCount; i < newCount; i++)
							{
								joinedItemSource.Insert(joinedIndex + i, itemValues->Get(start + i));
							}
						}
						else if (oldCount > newCount)
						{
							for (vint i = minCount; i < oldCount; i++)
							{
								joinedItemSource.RemoveAt(joinedIndex + i);
							}
						}

						cachedGroupItemCounts[index] += newCount - oldCount;
					}
				}

				vint GroupedDataSource::GetCountBeforeGroup(vint index)
				{
					vint count = 0;
					for (vint i = 0; i < index; i++)
					{
						count += cachedGroupItemCounts[i];
					}
					return count;
				}

				void GroupedDataSource::InsertGroupToJoined(vint index)
				{
					vint countBeforeGroup = GetCountBeforeGroup(index);
					auto group = groupedItemSource[index];
					vint itemCount = group->itemValues ? group->itemValues->GetCount() : 0;
					cachedGroupItemCounts.Insert(index, itemCount);

					if (itemCount > 0)
					{
						for (vint i = 0; i < itemCount; i++)
						{
							joinedItemSource.Insert(countBeforeGroup + i, group->itemValues->Get(i));
						}
					}
				}

				void GroupedDataSource::RemoveGroupFromJoined(vint index)
				{
					vint countBeforeGroup = GetCountBeforeGroup(index);
					joinedItemSource.RemoveRange(countBeforeGroup, cachedGroupItemCounts[index]);
					cachedGroupItemCounts.RemoveAt(index);
				}

				GroupedDataSource::GroupedDataSource(compositions::GuiGraphicsComposition* _associatedComposition)
					:associatedComposition(_associatedComposition)
				{
					GroupEnabledChanged.SetAssociatedComposition(associatedComposition);
					GroupTitlePropertyChanged.SetAssociatedComposition(associatedComposition);
					GroupChildrenPropertyChanged.SetAssociatedComposition(associatedComposition);

					groupChangedHandler = groupedItemSource.GetWrapper()->ItemChanged.Add(this, &GroupedDataSource::OnGroupChanged);
				}

				GroupedDataSource::~GroupedDataSource()
				{
					joinedItemSource.GetWrapper()->ItemChanged.Remove(groupChangedHandler);
				}

				Ptr<IValueEnumerable> GroupedDataSource::GetItemSource()
				{
					return itemSource;
				}

				void GroupedDataSource::SetItemSource(Ptr<IValueEnumerable> value)
				{
					if (itemSource != value)
					{
						itemSource = value;
						RebuildItemSource();
					}
				}

				bool GroupedDataSource::GetGroupEnabled()
				{
					return titleProperty && childrenProperty;
				}

				ItemProperty<WString> GroupedDataSource::GetGroupTitleProperty()
				{
					return titleProperty;
				}

				void GroupedDataSource::SetGroupTitleProperty(const ItemProperty<WString>& value)
				{
					if (titleProperty != value)
					{
						titleProperty = value;
						GroupTitlePropertyChanged.Execute(GuiEventArgs(associatedComposition));
						GroupEnabledChanged.Execute(GuiEventArgs(associatedComposition));
						RebuildItemSource();
					}
				}

				ItemProperty<Ptr<IValueEnumerable>> GroupedDataSource::GetGroupChildrenProperty()
				{
					return childrenProperty;
				}

				void GroupedDataSource::SetGroupChildrenProperty(const ItemProperty<Ptr<IValueEnumerable>>& value)
				{
					if (childrenProperty != value)
					{
						childrenProperty = value;
						GroupChildrenPropertyChanged.Execute(GuiEventArgs(associatedComposition));
						GroupEnabledChanged.Execute(GuiEventArgs(associatedComposition));
						RebuildItemSource();
					}
				}

				const GroupedDataSource::GalleryGroupList& GroupedDataSource::GetGroups()
				{
					return groupedItemSource;
				}
			}

/***********************************************************************
GuiBindableRibbonGalleryList
***********************************************************************/

			void GuiBindableRibbonGalleryList::BeforeControlTemplateUninstalled_()
			{
			}

			void GuiBindableRibbonGalleryList::AfterControlTemplateInstalled_(bool initialize)
			{
				auto ct = GetControlTemplateObject(true);
				itemList->SetControlTemplate(ct->GetItemListTemplate());
				subMenu->SetControlTemplate(ct->GetMenuTemplate());
				groupContainer->SetControlTemplate(ct->GetGroupContainerTemplate());
				MenuResetGroupTemplate();
				UpdateLayoutSizeOffset();
			}

			void GuiBindableRibbonGalleryList::UpdateLayoutSizeOffset()
			{
				auto cSize = itemList->GetContainerComposition()->GetBounds();
				auto bSize = itemList->GetBoundsComposition()->GetBounds();
				layout->SetSizeOffset(Size(bSize.Width() - cSize.Width(), bSize.Height() - cSize.Height()));

				if (layout->GetItemWidth() > 0)
				{
					vint columns = layout->GetVisibleItemCount();
					if (columns == 0) columns = 1;
					vint rows = (visibleItemCount + columns - 1) / columns;
					vint height = (vint)(layout->GetBounds().Height()*(rows + 0.5));
					groupContainer->GetBoundsComposition()->SetPreferredMinSize(Size(0, height));
				}
				else
				{
					groupContainer->GetBoundsComposition()->SetPreferredMinSize(Size(0, 0));
				}
			}

			void GuiBindableRibbonGalleryList::OnItemListSelectionChanged(compositions::GuiGraphicsComposition* sender, compositions::GuiEventArgs& arguments)
			{
				auto pos = IndexToGalleryPos(itemList->GetSelectedItemIndex());
				if (pos.group != -1 && pos.item != -1)
				{
					for (vint i = 0; i < groupedItemSource.Count(); i++)
					{
						auto group = groupedItemSource[i];
						if (group->GetItemValues())
						{
							vint count = group->GetItemValues()->GetCount();
							for (vint j = 0; j < count; j++)
							{
								auto background = MenuGetGroupItemBackground(i, j);
								background->SetSelected(pos.group == i && pos.item == j);
							}
						}
					}
				}
				
				if (!skipItemAppliedEvent && itemList->GetSelectedItemIndex() != -1)
				{
					GuiItemEventArgs itemAppliedArgs(boundsComposition);
					itemAppliedArgs.itemIndex = itemList->GetSelectedItemIndex();
					ItemApplied.Execute(itemAppliedArgs);
				}
				SelectionChanged.Execute(GetNotifyEventArguments());
			}

			void GuiBindableRibbonGalleryList::OnItemListItemMouseEnter(compositions::GuiGraphicsComposition* sender, compositions::GuiItemEventArgs& arguments)
			{
				StartPreview(arguments.itemIndex);
			}

			void GuiBindableRibbonGalleryList::OnItemListItemMouseLeave(compositions::GuiGraphicsComposition* sender, compositions::GuiItemEventArgs& arguments)
			{
				StopPreview(arguments.itemIndex);
			}

			void GuiBindableRibbonGalleryList::OnBoundsChanged(compositions::GuiGraphicsComposition* sender, compositions::GuiEventArgs& arguments)
			{
				UpdateLayoutSizeOffset();

				auto bounds = boundsComposition->GetBounds();
				subMenu->GetBoundsComposition()->SetPreferredMinSize(Size(bounds.Width() + 20, 1));

				for (vint i = 0; i < groupedItemSource.Count(); i++)
				{
					auto group = groupedItemSource[i];
					if (group->GetItemValues())
					{
						vint count = group->GetItemValues()->GetCount();
						for (vint j = 0; j < count; j++)
						{
							auto background = MenuGetGroupItemBackground(i, j);
							background->GetBoundsComposition()->SetPreferredMinSize(Size(0, bounds.Height()));
						}
					}
				}
			}

			void GuiBindableRibbonGalleryList::OnRequestedDropdown(compositions::GuiGraphicsComposition* sender, compositions::GuiEventArgs& arguments)
			{
				subMenu->ShowPopup(this, Point(0, 0));
			}

			void GuiBindableRibbonGalleryList::OnRequestedScrollUp(compositions::GuiGraphicsComposition* sender, compositions::GuiEventArgs& arguments)
			{
				itemListArranger->ScrollUp();
			}

			void GuiBindableRibbonGalleryList::OnRequestedScrollDown(compositions::GuiGraphicsComposition* sender, compositions::GuiEventArgs& arguments)
			{
				itemListArranger->ScrollDown();
			}

			void GuiBindableRibbonGalleryList::MenuResetGroupTemplate()
			{
				groupStack->SetItemTemplate([this](const Value& groupValue)->GuiTemplate*
				{
					auto group = UnboxValue<Ptr<list::GalleryGroup>>(groupValue);

					auto groupTemplate = new GuiTemplate;
					groupTemplate->SetMinSizeLimitation(GuiGraphicsComposition::LimitToElementAndChildren);

					auto groupContentStack = new GuiStackComposition;
					groupContentStack->SetMinSizeLimitation(GuiGraphicsComposition::LimitToElementAndChildren);
					groupContentStack->SetAlignmentToParent(Margin(0, 0, 0, 0));
					groupContentStack->SetDirection(GuiStackComposition::Vertical);
					{
						auto item = new GuiStackItemComposition;
						groupContentStack->AddChild(item);

						auto header = new GuiControl(theme::ThemeName::RibbonToolstripHeader);
						header->SetControlTemplate(GetControlTemplateObject(true)->GetHeaderTemplate());
						header->GetBoundsComposition()->SetAlignmentToParent(Margin(0, 0, 0, 0));
						header->SetText(group->GetName());
						item->AddChild(header->GetBoundsComposition());
					}
					if (itemStyle)
					{
						auto item = new GuiStackItemComposition;
						item->SetPreferredMinSize(Size(1, 1));
						groupContentStack->AddChild(item);

						auto groupItemFlow = new GuiRepeatFlowComposition();
						groupItemFlow->SetMinSizeLimitation(GuiGraphicsComposition::LimitToElementAndChildren);
						groupItemFlow->SetAlignmentToParent(Margin(0, 0, 0, 0));
						groupItemFlow->SetItemSource(group->GetItemValues());
						groupItemFlow->SetItemTemplate([=](const Value& groupItemValue)->GuiTemplate*
						{
							auto groupItemTemplate = new GuiTemplate;
							groupItemTemplate->SetMinSizeLimitation(GuiGraphicsComposition::LimitToElementAndChildren);

							auto backgroundButton = new GuiSelectableButton(theme::ThemeName::ListItemBackground);
							if (auto style = GetControlTemplateObject(true)->GetBackgroundTemplate())
							{
								backgroundButton->SetControlTemplate(style);
							}
							backgroundButton->SetAutoSelection(false);
							backgroundButton->Clicked.AttachLambda([=](GuiGraphicsComposition* sender, GuiEventArgs& arguments)
							{
								auto groupIndex = groupStack->GetStackItems().IndexOf(dynamic_cast<GuiStackItemComposition*>(groupTemplate->GetParent()));
								auto itemIndex = groupItemFlow->GetFlowItems().IndexOf(dynamic_cast<GuiFlowItemComposition*>(groupItemTemplate->GetParent()));
								auto index = GalleryPosToIndex({ groupIndex,itemIndex });
								itemList->SetSelected(index, true);
								itemList->EnsureItemVisible(index);
								subMenu->Close();
							});
							backgroundButton->GetBoundsComposition()->GetEventReceiver()->mouseEnter.AttachLambda([=](GuiGraphicsComposition* sender, GuiEventArgs& arguments)
							{
								auto groupIndex = groupStack->GetStackItems().IndexOf(dynamic_cast<GuiStackItemComposition*>(groupTemplate->GetParent()));
								auto itemIndex = groupItemFlow->GetFlowItems().IndexOf(dynamic_cast<GuiFlowItemComposition*>(groupItemTemplate->GetParent()));
								auto index = GalleryPosToIndex({ groupIndex,itemIndex });
								StartPreview(index);
							});
							backgroundButton->GetBoundsComposition()->GetEventReceiver()->mouseLeave.AttachLambda([=](GuiGraphicsComposition* sender, GuiEventArgs& arguments)
							{
								auto groupIndex = groupStack->GetStackItems().IndexOf(dynamic_cast<GuiStackItemComposition*>(groupTemplate->GetParent()));
								auto itemIndex = groupItemFlow->GetFlowItems().IndexOf(dynamic_cast<GuiFlowItemComposition*>(groupItemTemplate->GetParent()));
								auto index = GalleryPosToIndex({ groupIndex,itemIndex });
								StopPreview(index);
							});
							groupItemTemplate->AddChild(backgroundButton->GetBoundsComposition());

							auto itemTemplate = itemStyle(groupItemValue);
							itemTemplate->SetAlignmentToParent(Margin(0, 0, 0, 0));
							backgroundButton->GetContainerComposition()->AddChild(itemTemplate);

							return groupItemTemplate;
						});
						item->AddChild(groupItemFlow);
					}
					groupTemplate->AddChild(groupContentStack);

					return groupTemplate;
				});
			}

			GuiControl* GuiBindableRibbonGalleryList::MenuGetGroupHeader(vint groupIndex)
			{
				CHECK_ERROR(0 <= groupIndex && groupIndex < groupedItemSource.Count(), L"GuiBindableRibbonGalleryList::MenuGetGroupHeader(vint)#Group index out of range");
				auto stackItem = groupStack->GetStackItems()[groupIndex];
				auto groupTemplate = stackItem->Children()[0];
				auto groupContentStack = dynamic_cast<GuiStackComposition*>(groupTemplate->Children()[0]);
				auto groupHeaderItem = groupContentStack->GetStackItems()[0];
				auto groupHeader = groupHeaderItem->Children()[0]->GetAssociatedControl();
				CHECK_ERROR(groupHeader, L"GuiBindableRibbonGalleryList::MenuGetGroupHeader(vint)#Internal error.");
				return groupHeader;
			}

			compositions::GuiRepeatFlowComposition* GuiBindableRibbonGalleryList::MenuGetGroupFlow(vint groupIndex)
			{
				CHECK_ERROR(0 <= groupIndex && groupIndex < groupedItemSource.Count(), L"GuiBindableRibbonGalleryList::MenuGetGroupFlow(vint)#Group index out of range");
				if (!itemStyle) return nullptr;
				auto stackItem = groupStack->GetStackItems()[groupIndex];
				auto groupTemplate = stackItem->Children()[0];
				auto groupContentStack = dynamic_cast<GuiStackComposition*>(groupTemplate->Children()[0]);
				auto groupContentItem = groupContentStack->GetStackItems()[1];
				auto groupFlow = dynamic_cast<GuiRepeatFlowComposition*>(groupContentItem->Children()[0]);
				CHECK_ERROR(groupFlow, L"GuiBindableRibbonGalleryList::MenuGetGroupHeader(vint)#Internal error.");
				return groupFlow;
			}

			GuiSelectableButton* GuiBindableRibbonGalleryList::MenuGetGroupItemBackground(vint groupIndex, vint itemIndex)
			{
				CHECK_ERROR(0 <= groupIndex && groupIndex < groupedItemSource.Count(), L"GuiBindableRibbonGalleryList::MenuGetGroupItemBackground(vint, vint)#Group index out of range");
				auto group = groupedItemSource[groupIndex];
				CHECK_ERROR(group->GetItemValues() && 0 <= itemIndex && itemIndex < group->GetItemValues()->GetCount(), L"GuiBindableRibbonGalleryList::MenuGetGroupHeader(vint, vint)#Item index out of range");

				auto groupFlow = MenuGetGroupFlow(groupIndex);
				auto groupFlowItem = groupFlow->GetFlowItems()[itemIndex];
				auto groupItemTemplate = groupFlowItem->Children()[0];
				auto groupItemBackground = dynamic_cast<GuiSelectableButton*>(groupItemTemplate->Children()[0]->GetAssociatedControl());
				CHECK_ERROR(groupItemBackground, L"GuiBindableRibbonGalleryList::MenuGetGroupHeader(vint, vint)#Internal error.");
				return groupItemBackground;
			}

			void GuiBindableRibbonGalleryList::StartPreview(vint index)
			{
				if (index != itemList->GetSelectedItemIndex())
				{
					GuiItemEventArgs previewArgs(boundsComposition);
					previewArgs.itemIndex = index;
					PreviewStarted.Execute(previewArgs);
				}
			}

			void GuiBindableRibbonGalleryList::StopPreview(vint index)
			{
				if (index != itemList->GetSelectedItemIndex())
				{
					GuiItemEventArgs previewArgs(boundsComposition);
					previewArgs.itemIndex = index;
					PreviewStopped.Execute(previewArgs);
				}
			}

			GuiMenu* GuiBindableRibbonGalleryList::ProvideDropdownMenu()
			{
				return GetSubMenu();
			}

			GuiBindableRibbonGalleryList::GuiBindableRibbonGalleryList(theme::ThemeName themeName)
				:GuiRibbonGallery(themeName)
				, GroupedDataSource(boundsComposition)
			{
				ItemTemplateChanged.SetAssociatedComposition(boundsComposition);
				SelectionChanged.SetAssociatedComposition(boundsComposition);
				PreviewStarted.SetAssociatedComposition(boundsComposition);
				PreviewStopped.SetAssociatedComposition(boundsComposition);
				ItemApplied.SetAssociatedComposition(boundsComposition);
				subMenu = new GuiRibbonToolstripMenu(theme::ThemeName::RibbonToolstripMenu, this);

				{
					layout = new ribbon_impl::GalleryResponsiveLayout;
					layout->SetAlignmentToParent(Margin(0, 0, 0, 0));
					containerComposition->AddChild(layout);

					itemListArranger = new ribbon_impl::GalleryItemArranger(this);
					itemList = new GuiBindableTextList(theme::ThemeName::RibbonGalleryItemList);
					itemList->GetBoundsComposition()->SetAlignmentToParent(Margin(0, 0, 0, 0));
					itemList->SetArranger(itemListArranger);
					itemList->SetItemSource(joinedItemSource.GetWrapper());
					itemList->SelectionChanged.AttachMethod(this, &GuiBindableRibbonGalleryList::OnItemListSelectionChanged);
					itemList->ItemMouseEnter.AttachMethod(this, &GuiBindableRibbonGalleryList::OnItemListItemMouseEnter);
					itemList->ItemMouseLeave.AttachMethod(this, &GuiBindableRibbonGalleryList::OnItemListItemMouseLeave);
					layout->AddChild(itemList->GetBoundsComposition());
				}
				{
					groupContainer = new GuiScrollContainer(theme::ThemeName::ScrollView);
					groupContainer->SetHorizontalAlwaysVisible(false);
					groupContainer->SetVerticalAlwaysVisible(false);
					groupContainer->SetExtendToFullWidth(true);
					groupContainer->GetBoundsComposition()->SetAlignmentToParent(Margin(0, 0, 0, 0));
					subMenu->GetContentComposition()->AddChild(groupContainer->GetBoundsComposition());

					groupStack = new GuiRepeatStackComposition();
					groupStack->SetMinSizeLimitation(GuiGraphicsComposition::LimitToElementAndChildren);
					groupStack->SetAlignmentToParent(Margin(0, 0, 0, 0));
					groupStack->SetDirection(GuiStackComposition::Vertical);
					groupStack->SetItemSource(groupedItemSource.GetWrapper());
					groupContainer->GetContainerComposition()->AddChild(groupStack);
					MenuResetGroupTemplate();
				}

				RequestedScrollUp.AttachMethod(this, &GuiBindableRibbonGalleryList::OnRequestedScrollUp);
				RequestedScrollDown.AttachMethod(this, &GuiBindableRibbonGalleryList::OnRequestedScrollDown);
				RequestedDropdown.AttachMethod(this, &GuiBindableRibbonGalleryList::OnRequestedDropdown);
				boundsComposition->BoundsChanged.AttachMethod(this, &GuiBindableRibbonGalleryList::OnBoundsChanged);
				itemListArranger->UnblockScrollUpdate();
			}

			GuiBindableRibbonGalleryList::~GuiBindableRibbonGalleryList()
			{
				delete subMenu;
			}

			GuiBindableRibbonGalleryList::ItemStyleProperty GuiBindableRibbonGalleryList::GetItemTemplate()
			{
				return itemStyle;
			}

			void GuiBindableRibbonGalleryList::SetItemTemplate(ItemStyleProperty value)
			{
				if (itemStyle != value)
				{
					itemStyle = value;
					itemList->SetItemTemplate(value);
					ItemTemplateChanged.Execute(GetNotifyEventArguments());
				}
			}

			GalleryPos GuiBindableRibbonGalleryList::IndexToGalleryPos(vint index)
			{
				if (0 <= index && index < joinedItemSource.Count())
				{
					FOREACH_INDEXER(Ptr<list::GalleryGroup>, group, groupIndex, groupedItemSource)
					{
						auto itemValues = group->GetItemValues();
						vint itemCount = itemValues ? itemValues->GetCount() : 0;
						if (index >= itemCount)
						{
							index -= itemCount;
						}
						else
						{
							return GalleryPos(groupIndex, index);
						}
					}
				}
				return {};
			}

			vint GuiBindableRibbonGalleryList::GalleryPosToIndex(GalleryPos pos)
			{
				if (0 <= pos.group && pos.group < groupedItemSource.Count())
				{
					auto countBeforeGroup = GetCountBeforeGroup(pos.group);
					auto itemValues = groupedItemSource[pos.group]->GetItemValues();
					vint itemCount = itemValues ? itemValues->GetCount() : 0;
					if (0 <= pos.item && pos.item < itemCount)
					{
						return countBeforeGroup + pos.item;
					}
				}
				return -1;
			}

			vint GuiBindableRibbonGalleryList::GetMinCount()
			{
				return layout->GetMinCount();
			}

			void GuiBindableRibbonGalleryList::SetMinCount(vint value)
			{
				layout->SetMinCount(value);
			}

			vint GuiBindableRibbonGalleryList::GetMaxCount()
			{
				return layout->GetMaxCount();
			}

			void GuiBindableRibbonGalleryList::SetMaxCount(vint value)
			{
				layout->SetMaxCount(value);
			}

			vint GuiBindableRibbonGalleryList::GetSelectedIndex()
			{
				return itemList->GetSelectedItemIndex();
			}

			description::Value GuiBindableRibbonGalleryList::GetSelectedItem()
			{
				vint index = itemList->GetSelectedItemIndex();
				if (index == -1) return Value();

				auto pos = IndexToGalleryPos(index);
				return groupedItemSource[pos.group]->GetItemValues()->Get(pos.item);
			}

			void GuiBindableRibbonGalleryList::ApplyItem(vint index)
			{
				if (index == -1)
				{
					itemList->ClearSelection();
				}
				else
				{
					itemList->SetSelected(index, true);
					itemList->EnsureItemVisible(index);
				}
			}

			void GuiBindableRibbonGalleryList::SelectItem(vint index)
			{
				skipItemAppliedEvent = true;
				ApplyItem(index);
				skipItemAppliedEvent = false;
			}

			vint GuiBindableRibbonGalleryList::GetVisibleItemCount()
			{
				return visibleItemCount;
			}

			void GuiBindableRibbonGalleryList::SetVisibleItemCount(vint value)
			{
				if (visibleItemCount != value)
				{
					visibleItemCount = value;
					UpdateLayoutSizeOffset();
				}
			}

			GuiToolstripMenu* GuiBindableRibbonGalleryList::GetSubMenu()
			{
				return subMenu;
			}

			IDescriptable* GuiBindableRibbonGalleryList::QueryService(const WString& identifier)
			{
				if (identifier == IGuiMenuDropdownProvider::Identifier)
				{
					return (IGuiMenuDropdownProvider*)this;
				}
				else
				{
					return GuiRibbonGallery::QueryService(identifier);
				}
			}
		}
	}
}

/***********************************************************************
.\CONTROLS\TOOLSTRIPPACKAGE\GUIRIBBONIMPL.CPP
***********************************************************************/

namespace vl
{
	namespace presentation
	{
		namespace controls
		{
			using namespace compositions;

/***********************************************************************
GalleryItemArranger
***********************************************************************/

			namespace ribbon_impl
			{
				void GalleryItemArranger::BeginPlaceItem(bool forMoving, Rect newBounds, vint& newStartIndex)
				{
					if (forMoving)
					{
						pim_itemWidth = itemWidth;
						newStartIndex = firstIndex;
					}
				}

				void GalleryItemArranger::PlaceItem(bool forMoving, vint index, ItemStyleRecord style, Rect viewBounds, Rect& bounds, Margin& alignmentToParent)
				{
					alignmentToParent = Margin(-1, 0, -1, 0);
					bounds = Rect(Point((index - firstIndex) * itemWidth, 0), Size(itemWidth, 0));

					if (forMoving)
					{
						vint styleWidth = callback->GetStylePreferredSize(GetStyleBounds(style)).x;
						if (pim_itemWidth < styleWidth)
						{
							pim_itemWidth = styleWidth;
						}
					}
				}

				bool GalleryItemArranger::IsItemOutOfViewBounds(vint index, ItemStyleRecord style, Rect bounds, Rect viewBounds)
				{
					return bounds.Right() + pim_itemWidth > viewBounds.Right();
				}

				bool GalleryItemArranger::EndPlaceItem(bool forMoving, Rect newBounds, vint newStartIndex)
				{
					bool result = false;
					if (forMoving)
					{
						if (pim_itemWidth != itemWidth)
						{
							itemWidth = pim_itemWidth;
							result = true;
						}
					}

					if (!blockScrollUpdate)
					{
						UnblockScrollUpdate();
					}

					return result;
				}

				void GalleryItemArranger::InvalidateItemSizeCache()
				{
					itemWidth = 1;
				}

				Size GalleryItemArranger::OnCalculateTotalSize()
				{
					return Size(1, 1);
				}

				GalleryItemArranger::GalleryItemArranger(GuiBindableRibbonGalleryList* _owner)
					:owner(_owner)
				{
				}

				GalleryItemArranger::~GalleryItemArranger()
				{
				}

				vint GalleryItemArranger::FindItem(vint itemIndex, compositions::KeyDirection key)
				{
					vint count = itemProvider->Count();
					vint groupCount = viewBounds.Width() / itemWidth;

					switch (key)
					{
					case KeyDirection::Left:
						itemIndex--;
						break;
					case KeyDirection::Right:
						itemIndex++;
						break;
					case KeyDirection::Home:
						itemIndex = 0;
						break;
					case KeyDirection::End:
						itemIndex = count;
						break;
					case KeyDirection::PageUp:
						itemIndex -= groupCount;
						break;
					case KeyDirection::PageDown:
						itemIndex += groupCount;
						break;
					default:
						return -1;
					}

					if (itemIndex < 0) return 0;
					else if (itemIndex >= count) return count - 1;
					else return itemIndex;
				}

				bool GalleryItemArranger::EnsureItemVisible(vint itemIndex)
				{
					if (callback && 0 <= itemIndex && itemIndex < itemProvider->Count())
					{
						vint groupCount = viewBounds.Width() / itemWidth;
						if (itemIndex < firstIndex)
						{
							firstIndex = itemIndex;
							callback->OnTotalSizeChanged();
						}
						else if (itemIndex >= firstIndex + groupCount)
						{
							firstIndex = itemIndex - groupCount + 1;
							callback->OnTotalSizeChanged();
						}
						return true;
					}
					return false;
				}

				Size GalleryItemArranger::GetAdoptedSize(Size expectedSize)
				{
					return Size(1, 1);
				}

				void GalleryItemArranger::ScrollUp()
				{
					vint count = itemProvider->Count();
					vint groupCount = viewBounds.Width() / itemWidth;
					if (count > groupCount)
					{
						firstIndex -= groupCount;
						if (firstIndex < 0)
						{
							firstIndex = 0;
						}

						if (callback)
						{
							callback->OnTotalSizeChanged();
						}
					}
				}

				void GalleryItemArranger::ScrollDown()
				{
					vint count = itemProvider->Count();
					vint groupCount = viewBounds.Width() / itemWidth;
					if (count > groupCount)
					{
						firstIndex += groupCount;
						if (firstIndex > count - groupCount)
						{
							firstIndex = count - groupCount;
						}

						if (callback)
						{
							callback->OnTotalSizeChanged();
						}
					}
				}

				void GalleryItemArranger::UnblockScrollUpdate()
				{
					blockScrollUpdate = false;

					vint count = itemProvider->Count();
					vint groupCount = viewBounds.Width() / pim_itemWidth;
					owner->SetScrollUpEnabled(firstIndex > 0);
					owner->SetScrollDownEnabled(firstIndex + groupCount < count);
					if (owner->layout->GetItemWidth() != pim_itemWidth)
					{
						owner->layout->SetItemWidth(pim_itemWidth);
						owner->UpdateLayoutSizeOffset();
					}
				}

/***********************************************************************
GalleryResponsiveLayout
***********************************************************************/

				void GalleryResponsiveLayout::UpdateMinSize()
				{
					SetPreferredMinSize(Size(itemCount * itemWidth + sizeOffset.x, sizeOffset.y));
				}

				GalleryResponsiveLayout::GalleryResponsiveLayout()
				{
					SetDirection(ResponsiveDirection::Horizontal);
				}

				GalleryResponsiveLayout::~GalleryResponsiveLayout()
				{
				}

				vint GalleryResponsiveLayout::GetMinCount()
				{
					return minCount;
				}

				vint GalleryResponsiveLayout::GetMaxCount()
				{
					return maxCount;
				}

				vint GalleryResponsiveLayout::GetItemWidth()
				{
					return itemWidth;
				}

				Size GalleryResponsiveLayout::GetSizeOffset()
				{
					return sizeOffset;
				}

				vint GalleryResponsiveLayout::GetVisibleItemCount()
				{
					return itemCount;
				}

				void GalleryResponsiveLayout::SetMinCount(vint value)
				{
					vint oldCount = GetLevelCount();
					vint oldLevel = GetCurrentLevel();

					if (minCount != value)
					{
						if (value < 0) value = 0;
						minCount = value;
						if (maxCount < minCount) maxCount = minCount;
						if (itemCount < minCount) itemCount = minCount;
						UpdateMinSize();
					}

					bool countChanged = oldCount != GetLevelCount();
					bool levelChanged = oldLevel != GetCurrentLevel();
					if (countChanged) LevelCountChanged.Execute(GuiEventArgs(this));
					if (levelChanged) CurrentLevelChanged.Execute(GuiEventArgs(this));
					if (countChanged || levelChanged) OnResponsiveChildLevelUpdated();
				}

				void GalleryResponsiveLayout::SetMaxCount(vint value)
				{
					vint oldCount = GetLevelCount();
					vint oldLevel = GetCurrentLevel();

					if (maxCount != value)
					{
						if (value < 0) value = 0;
						maxCount = value;
						if (minCount > maxCount) minCount = maxCount;
						if (itemCount > maxCount) itemCount = maxCount;
						UpdateMinSize();
					}

					if (oldCount != GetLevelCount()) LevelCountChanged.Execute(GuiEventArgs(this));
					if (oldLevel != GetCurrentLevel()) CurrentLevelChanged.Execute(GuiEventArgs(this));
				}

				void GalleryResponsiveLayout::SetItemWidth(vint value)
				{
					if (itemWidth != value)
					{
						itemWidth = value;
						UpdateMinSize();
					}
				}

				void GalleryResponsiveLayout::SetSizeOffset(Size value)
				{
					if (sizeOffset != value)
					{
						sizeOffset = value;
						UpdateMinSize();
					}
				}

				vint GalleryResponsiveLayout::GetLevelCount()
				{
					return maxCount - minCount + 1;
				}

				vint GalleryResponsiveLayout::GetCurrentLevel()
				{
					return itemCount - minCount;
				}

				bool GalleryResponsiveLayout::LevelDown()
				{
					if (itemCount > minCount)
					{
						itemCount--;
						UpdateMinSize();
						CurrentLevelChanged.Execute(GuiEventArgs(this));
						return true;
					}
					return false;
				}

				bool GalleryResponsiveLayout::LevelUp()
				{
					if (itemCount < maxCount)
					{
						itemCount++;
						UpdateMinSize();
						CurrentLevelChanged.Execute(GuiEventArgs(this));
						return true;
					}
					return false;
				}
			}
		}
	}
}

/***********************************************************************
.\CONTROLS\TOOLSTRIPPACKAGE\GUITOOLSTRIPCOMMAND.CPP
***********************************************************************/

namespace vl
{
	namespace presentation
	{
		namespace controls
		{
			using namespace collections;
			using namespace compositions;
			using namespace regex;
			using namespace parsing;

/***********************************************************************
GuiToolstripCommand
***********************************************************************/

			void GuiToolstripCommand::OnShortcutKeyItemExecuted(compositions::GuiGraphicsComposition* sender, compositions::GuiEventArgs& arguments)
			{
				Executed.ExecuteWithNewSender(arguments, sender);
			}

			void GuiToolstripCommand::OnRenderTargetChanged(compositions::GuiGraphicsComposition* sender, compositions::GuiEventArgs& arguments)
			{
				UpdateShortcutOwner();
			}

			void GuiToolstripCommand::InvokeDescriptionChanged()
			{
				GuiEventArgs arguments;
				DescriptionChanged.Execute(arguments);
			}

			void GuiToolstripCommand::ReplaceShortcut(compositions::IGuiShortcutKeyItem* value, Ptr<ShortcutBuilder> builder)
			{
				if (shortcutKeyItem != value)
				{
					if (shortcutKeyItem)
					{
						shortcutKeyItem->Executed.Detach(shortcutKeyItemExecutedHandler);
						if (shortcutBuilder)
						{
							auto manager = dynamic_cast<GuiShortcutKeyManager*>(shortcutOwner->GetShortcutKeyManager());
							if (manager)
							{
								manager->DestroyShortcut(shortcutBuilder->ctrl, shortcutBuilder->shift, shortcutBuilder->alt, shortcutBuilder->key);
							}
						}
					}
					shortcutKeyItem = nullptr;
					shortcutKeyItemExecutedHandler = nullptr;
					shortcutBuilder = value ? builder : nullptr;
					if (value)
					{
						shortcutKeyItem = value;
						shortcutKeyItemExecutedHandler = shortcutKeyItem->Executed.AttachMethod(this, &GuiToolstripCommand::OnShortcutKeyItemExecuted);
					}
					InvokeDescriptionChanged();
				}
			}

			void GuiToolstripCommand::BuildShortcut(const WString& builderText)
			{
				List<Ptr<ParsingError>> errors;
				if (auto parser = GetParserManager()->GetParser<ShortcutBuilder>(L"SHORTCUT"))
				{
					if (Ptr<ShortcutBuilder> builder = parser->ParseInternal(builderText, errors))
					{
						if (shortcutOwner)
						{
							if (!shortcutOwner->GetShortcutKeyManager())
							{
								shortcutOwner->SetShortcutKeyManager(new GuiShortcutKeyManager);
							}
							if (auto manager = dynamic_cast<GuiShortcutKeyManager*>(shortcutOwner->GetShortcutKeyManager()))
							{
								IGuiShortcutKeyItem* item = manager->TryGetShortcut(builder->ctrl, builder->shift, builder->alt, builder->key);
								if (!item)
								{
									item = manager->CreateShortcut(builder->ctrl, builder->shift, builder->alt, builder->key);
									if (item)
									{
										ReplaceShortcut(item, builder);
									}
								}
							}
						}
						else
						{
							shortcutBuilder = builder;
						}
					}
				}
			}

			void GuiToolstripCommand::UpdateShortcutOwner()
			{
				GuiControlHost* host = nullptr;
				if (auto control = dynamic_cast<GuiControl*>(attachedRootObject))
				{
					host = control->GetRelatedControlHost();
				}
				else if (auto composition = dynamic_cast<GuiGraphicsComposition*>(attachedRootObject))
				{
					host = composition->GetRelatedControlHost();
				}

				if (shortcutOwner != host)
				{
					if (shortcutOwner)
					{
						ReplaceShortcut(nullptr, nullptr);
						shortcutOwner = nullptr;
					}
					shortcutOwner = host;
					if (shortcutBuilder && !shortcutKeyItem)
					{
						BuildShortcut(shortcutBuilder->text);
					}
				}
			}

			GuiToolstripCommand::GuiToolstripCommand()
			{
			}

			GuiToolstripCommand::~GuiToolstripCommand()
			{
			}

			void GuiToolstripCommand::Attach(GuiInstanceRootObject* rootObject)
			{
				GuiGraphicsComposition* rootComposition = nullptr;

				if (attachedRootObject != rootObject)
				{
					if (attachedRootObject)
					{
						if (auto control = dynamic_cast<GuiControl*>(attachedRootObject))
						{
							control->RenderTargetChanged.Detach(renderTargetChangedHandler);
						}
						else if (auto composition = dynamic_cast<GuiGraphicsComposition*>(attachedRootObject))
						{
							composition->GetEventReceiver()->renderTargetChanged.Detach(renderTargetChangedHandler);
						}
						renderTargetChangedHandler = nullptr;
					}

					attachedRootObject = rootObject;
					if (attachedRootObject)
					{
						if (auto control = dynamic_cast<GuiControl*>(attachedRootObject))
						{
							renderTargetChangedHandler = control->RenderTargetChanged.AttachMethod(this, &GuiToolstripCommand::OnRenderTargetChanged);
						}
						else if (auto composition = dynamic_cast<GuiGraphicsComposition*>(attachedRootObject))
						{
							renderTargetChangedHandler = composition->GetEventReceiver()->renderTargetChanged.AttachMethod(this, &GuiToolstripCommand::OnRenderTargetChanged);
						}
					}
					UpdateShortcutOwner();
				}
			}

			void GuiToolstripCommand::Detach(GuiInstanceRootObject* rootObject)
			{
				Attach(nullptr);
			}

			Ptr<GuiImageData> GuiToolstripCommand::GetLargeImage()
			{
				return largeImage;
			}

			void GuiToolstripCommand::SetLargeImage(Ptr<GuiImageData> value)
			{
				if (largeImage != value)
				{
					largeImage = value;
					InvokeDescriptionChanged();
				}
			}

			Ptr<GuiImageData> GuiToolstripCommand::GetImage()
			{
				return image;
			}

			void GuiToolstripCommand::SetImage(Ptr<GuiImageData> value)
			{
				if(image!=value)
				{
					image=value;
					InvokeDescriptionChanged();
				}
			}

			const WString& GuiToolstripCommand::GetText()
			{
				return text;
			}

			void GuiToolstripCommand::SetText(const WString& value)
			{
				if(text!=value)
				{
					text=value;
					InvokeDescriptionChanged();
				}
			}

			compositions::IGuiShortcutKeyItem* GuiToolstripCommand::GetShortcut()
			{
				return shortcutKeyItem;
			}

			void GuiToolstripCommand::SetShortcut(compositions::IGuiShortcutKeyItem* value)
			{
				ReplaceShortcut(value, 0);
			}

			WString GuiToolstripCommand::GetShortcutBuilder()
			{
				return shortcutBuilder ? shortcutBuilder->text : L"";
			}

			void GuiToolstripCommand::SetShortcutBuilder(const WString& value)
			{
				BuildShortcut(value);
			}

			bool GuiToolstripCommand::GetEnabled()
			{
				return enabled;
			}

			void GuiToolstripCommand::SetEnabled(bool value)
			{
				if(enabled!=value)
				{
					enabled=value;
					InvokeDescriptionChanged();
				}
			}

			bool GuiToolstripCommand::GetSelected()
			{
				return selected;
			}

			void GuiToolstripCommand::SetSelected(bool value)
			{
				if(selected!=value)
				{
					selected=value;
					InvokeDescriptionChanged();
				}
			}

/***********************************************************************
GuiToolstripCommand::ShortcutBuilder Parser
***********************************************************************/

			class GuiToolstripCommandShortcutParser : public Object, public IGuiParser<GuiToolstripCommand::ShortcutBuilder>
			{
				typedef GuiToolstripCommand::ShortcutBuilder			ShortcutBuilder;
			public:
				Regex						regexShortcut;

				GuiToolstripCommandShortcutParser()
					:regexShortcut(L"((<ctrl>Ctrl)/+|(<shift>Shift)/+|(<alt>Alt)/+)*(<key>/.+)")
				{
				}

				Ptr<ShortcutBuilder> ParseInternal(const WString& text, collections::List<Ptr<ParsingError>>& errors)override
				{
					Ptr<RegexMatch> match=regexShortcut.MatchHead(text);
					if (match && match->Result().Length() != text.Length())
					{
						errors.Add(new ParsingError(L"Failed to parse a shortcut \"" + text + L"\"."));
						return 0;
					}

					Ptr<ShortcutBuilder> builder = new ShortcutBuilder;
					builder->text = text;
					builder->ctrl = match->Groups().Contains(L"ctrl");
					builder->shift = match->Groups().Contains(L"shift");
					builder->alt = match->Groups().Contains(L"alt");

					WString name = match->Groups()[L"key"][0].Value();
					builder->key = GetCurrentController()->InputService()->GetKey(name);

					return builder->key == -1 ? nullptr : builder;
				}
			};

/***********************************************************************
GuiToolstripCommandPlugin
***********************************************************************/

			class GuiToolstripCommandPlugin : public Object, public IGuiPlugin
			{
			public:

				GUI_PLUGIN_NAME(GacUI_Compiler_ShortcutParser)
				{
					GUI_PLUGIN_DEPEND(GacUI_Parser);
				}
				
				void Load()override
				{
					IGuiParserManager* manager=GetParserManager();
					manager->SetParser(L"SHORTCUT", new GuiToolstripCommandShortcutParser);
				}

				void Unload()override
				{
				}
			};
			GUI_REGISTER_PLUGIN(GuiToolstripCommandPlugin)
		}
	}
}

/***********************************************************************
.\RESOURCES\GUIDOCUMENT_LOAD.CPP
***********************************************************************/

namespace vl
{
	namespace presentation
	{
		using namespace collections;
		using namespace parsing::tabling;
		using namespace parsing::xml;
		using namespace regex;

/***********************************************************************
document_operation_visitors::DeserializeNodeVisitor
***********************************************************************/

		namespace document_operation_visitors
		{
			class DeserializeNodeVisitor : public XmlNode::IVisitor
			{
			public:
				Ptr<DocumentModel>					model;
				Ptr<DocumentContainerRun>			container;
				vint								paragraphIndex;
				Ptr<GuiResourceItem>				resource;
				Ptr<GuiResourcePathResolver>		resolver;
				Regex								regexAttributeApply;
				GuiResourceError::List&				errors;

				DeserializeNodeVisitor(Ptr<DocumentModel> _model, Ptr<DocumentParagraphRun> _paragraph, vint _paragraphIndex, Ptr<GuiResourceItem> _resource, Ptr<GuiResourcePathResolver> _resolver, GuiResourceError::List& _errors)
					:model(_model)
					, container(_paragraph)
					, paragraphIndex(_paragraphIndex)
					, resource(_resource)
					, resolver(_resolver)
					, regexAttributeApply(L"/{@(<value>[^{}]+)/}")
					, errors(_errors)
				{
				}

				void PrintText(const WString& text)
				{
					Ptr<DocumentTextRun> run = new DocumentTextRun;
					run->text = text;
					container->runs.Add(run);
				}

				void Visit(XmlText* node)override
				{
					PrintText(node->content.value);
				}

				void Visit(XmlCData* node)override
				{
					PrintText(node->content.value);
				}

				void Visit(XmlAttribute* node)override
				{
				}

				void Visit(XmlComment* node)override
				{
				}

				void Visit(XmlElement* node)override
				{
					Ptr<DocumentContainerRun> createdContainer;
					bool useTemplateInfo = false;
					XmlElement* subNodeContainer = node;

					if (node->name.value == L"br")
					{
						PrintText(L"\r\n");
					}
					else if (node->name.value == L"sp")
					{
						PrintText(L" ");
					}
					else if (node->name.value == L"tab")
					{
						PrintText(L"\t");
					}
					else if (node->name.value == L"img")
					{
						Ptr<DocumentImageRun> run = new DocumentImageRun;
						run->baseline = -1;

						if (Ptr<XmlAttribute> source = XmlGetAttribute(node, L"source"))
						{
							run->source = source->value.value;
							WString protocol, path;
							if (IsResourceUrl(run->source, protocol, path))
							{
								Ptr<GuiImageData> imageData = resolver->ResolveResource(protocol, path).Cast<GuiImageData>();
								if (imageData)
								{
									run->image = imageData->GetImage();
								}
								if (run->image && run->image->GetFrameCount() > 0)
								{
									run->size = run->image->GetFrame(0)->GetSize();
									run->frameIndex = 0;
								}
							}

							FOREACH(Ptr<XmlAttribute>, att, node->attributes)
							{
								if (att->name.value == L"width")
								{
									run->size.x = wtoi(att->value.value);
								}
								else if (att->name.value == L"height")
								{
									run->size.y = wtoi(att->value.value);
								}
								else if (att->name.value == L"baseline")
								{
									run->baseline = wtoi(att->value.value);
								}
								else if (att->name.value == L"frameIndex")
								{
									run->frameIndex = wtoi(att->value.value);
								}
								else if (att->name.value != L"source")
								{
									errors.Add(GuiResourceError({ {resource},att->name.codeRange.start }, L"Unknown attribute in <img>: \"" + att->name.value + L"\"."));
								}
							}

							container->runs.Add(run);
						}
						else
						{
							errors.Add(GuiResourceError({ {resource},node->codeRange.start }, L"Attribute \"source\" is missing in <img>."));
						}
					}
					else if (node->name.value == L"object")
					{
						Ptr<DocumentEmbeddedObjectRun> run = new DocumentEmbeddedObjectRun;
						run->baseline = -1;

						if (auto name = XmlGetAttribute(node, L"name"))
						{
							run->name = name->value.value;
							container->runs.Add(run);
						}
						else
						{
							errors.Add(GuiResourceError({ {resource},node->codeRange.start }, L"The \"name\" attribute in <object> is missing."));
						}
					}
					else if (node->name.value == L"font")
					{
						Ptr<DocumentStylePropertiesRun> run = new DocumentStylePropertiesRun();
						Ptr<DocumentStyleProperties> sp = new DocumentStyleProperties;
						run->style = sp;

						FOREACH(Ptr<XmlAttribute>, att, node->attributes)
						{
							if (att->name.value == L"face")
							{
								sp->face = att->value.value;
							}
							else if (att->name.value == L"size")
							{
								sp->size = DocumentFontSize::Parse(att->value.value);
							}
							else if (att->name.value == L"color")
							{
								sp->color = Color::Parse(att->value.value);
							}
							else if (att->name.value == L"bkcolor")
							{
								sp->backgroundColor = Color::Parse(att->value.value);
							}
							else
							{
								errors.Add(GuiResourceError({ {resource},att->name.codeRange.start }, L"Unknown attribute in <font>: \"" + att->name.value + L"\"."));
							}
						}
						container->runs.Add(run);
						createdContainer = run;
					}
					else if (node->name.value == L"b" || node->name.value == L"b-")
					{
						Ptr<DocumentStylePropertiesRun> run = new DocumentStylePropertiesRun();
						run->style = new DocumentStyleProperties;
						run->style->bold = node->name.value == L"b";
						container->runs.Add(run);
						createdContainer = run;
					}
					else if (node->name.value == L"i" || node->name.value == L"i-")
					{
						Ptr<DocumentStylePropertiesRun> run = new DocumentStylePropertiesRun();
						run->style = new DocumentStyleProperties;
						run->style->italic = node->name.value == L"i";
						container->runs.Add(run);
						createdContainer = run;
					}
					else if (node->name.value == L"u" || node->name.value == L"u-")
					{
						Ptr<DocumentStylePropertiesRun> run = new DocumentStylePropertiesRun();
						run->style = new DocumentStyleProperties;
						run->style->underline = node->name.value == L"u";
						container->runs.Add(run);
						createdContainer = run;
					}
					else if (node->name.value == L"s" || node->name.value == L"s-")
					{
						Ptr<DocumentStylePropertiesRun> run = new DocumentStylePropertiesRun();
						run->style = new DocumentStyleProperties;
						run->style->strikeline = node->name.value == L"s";
						container->runs.Add(run);
						createdContainer = run;
					}
					else if (node->name.value == L"ha")
					{
						Ptr<DocumentStylePropertiesRun> run = new DocumentStylePropertiesRun();
						run->style = new DocumentStyleProperties;
						run->style->antialias = true;
						run->style->verticalAntialias = false;
						container->runs.Add(run);
						createdContainer = run;
					}
					else if (node->name.value == L"va")
					{
						Ptr<DocumentStylePropertiesRun> run = new DocumentStylePropertiesRun();
						run->style = new DocumentStyleProperties;
						run->style->antialias = true;
						run->style->verticalAntialias = true;
						container->runs.Add(run);
						createdContainer = run;
					}
					else if (node->name.value == L"na")
					{
						Ptr<DocumentStylePropertiesRun> run = new DocumentStylePropertiesRun();
						run->style = new DocumentStyleProperties;
						run->style->antialias = false;
						run->style->verticalAntialias = false;
						container->runs.Add(run);
						createdContainer = run;
					}
					else if (node->name.value == L"div")
					{
						if (Ptr<XmlAttribute> att = XmlGetAttribute(node, L"style"))
						{
							WString styleName = att->value.value;

							Ptr<DocumentStyleApplicationRun> run = new DocumentStyleApplicationRun;
							run->styleName = styleName;
							container->runs.Add(run);
							createdContainer = run;
						}
						else
						{
							createdContainer = container;
						}
					}
					else if (node->name.value == L"a")
					{
						Ptr<DocumentHyperlinkRun> run = new DocumentHyperlinkRun;
						run->normalStyleName = L"#NormalLink";
						run->activeStyleName = L"#ActiveLink";
						if (Ptr<XmlAttribute> att = XmlGetAttribute(node, L"normal"))
						{
							run->normalStyleName = att->value.value;
						}
						if (Ptr<XmlAttribute> att = XmlGetAttribute(node, L"active"))
						{
							run->activeStyleName = att->value.value;
						}
						if (Ptr<XmlAttribute> att = XmlGetAttribute(node, L"href"))
						{
							run->reference = att->value.value;
						}
						run->styleName = run->normalStyleName;
						container->runs.Add(run);
						createdContainer = run;
					}
					else if (node->name.value == L"p")
					{
						FOREACH(Ptr<XmlNode>, sub, node->subNodes)
						{
							sub->Accept(this);
						}
					}
					else
					{
						if (node->name.value != L"nop")
						{
							errors.Add(GuiResourceError({ {resource},node->codeRange.start }, L"Unknown element in <p>: \"" + node->name.value + L"\"."));
						}
						FOREACH(Ptr<XmlNode>, sub, node->subNodes)
						{
							sub->Accept(this);
						}
					}

					if (createdContainer)
					{
						Ptr<DocumentContainerRun> oldContainer = container;
						container = createdContainer;
						FOREACH(Ptr<XmlNode>, subNode, subNodeContainer->subNodes)
						{
							subNode->Accept(this);
						}
						container = oldContainer;
					}
				}

				void Visit(XmlInstruction* node)override
				{
				}

				void Visit(XmlDocument* node)override
				{
				}
			};

			Ptr<DocumentStyle> ParseDocumentStyle(Ptr<GuiResourceItem> resource, Ptr<XmlElement> styleElement, GuiResourceError::List& errors)
			{
				Ptr<DocumentStyle> style=new DocumentStyle;

				if(Ptr<XmlAttribute> parent=XmlGetAttribute(styleElement, L"parent"))
				{
					style->parentStyleName=parent->value.value;
				}

				Ptr<DocumentStyleProperties> sp=new DocumentStyleProperties;
				style->styles=sp;

				FOREACH(Ptr<XmlElement>, att, XmlGetElements(styleElement))
				{
					if(att->name.value==L"face")
					{
						sp->face=XmlGetValue(att);
					}
					else if(att->name.value==L"size")
					{
						sp->size=DocumentFontSize::Parse(XmlGetValue(att));
					}
					else if(att->name.value==L"color")
					{
						sp->color=Color::Parse(XmlGetValue(att));
					}
					else if(att->name.value==L"bkcolor")
					{
						sp->backgroundColor=Color::Parse(XmlGetValue(att));
					}
					else if(att->name.value==L"b")
					{
						sp->bold=XmlGetValue(att)==L"true";
					}
					else if(att->name.value==L"i")
					{
						sp->italic=XmlGetValue(att)==L"true";
					}
					else if(att->name.value==L"u")
					{
						sp->underline=XmlGetValue(att)==L"true";
					}
					else if(att->name.value==L"s")
					{
						sp->strikeline=XmlGetValue(att)==L"true";
					}
					else if(att->name.value==L"antialias")
					{
						WString value=XmlGetValue(att);
						if(value==L"horizontal" || value==L"default")
						{
							sp->antialias=true;
							sp->verticalAntialias=false;
						}
						else if(value==L"no")
						{
							sp->antialias=false;
							sp->verticalAntialias=false;
						}
						else if(value==L"vertical")
						{
							sp->antialias=true;
							sp->verticalAntialias=true;
						}
					}
					else
					{
						errors.Add(GuiResourceError({ {resource},att->codeRange.start }, L"Unknown element in <Style>: \"" + att->name.value + L"\"."));
					}
				}

				return style;
			}
		}
		using namespace document_operation_visitors;

/***********************************************************************
DocumentModel
***********************************************************************/

		Ptr<DocumentModel> DocumentModel::LoadFromXml(Ptr<GuiResourceItem> resource, Ptr<parsing::xml::XmlDocument> xml, Ptr<GuiResourcePathResolver> resolver, GuiResourceError::List& errors)
		{
			Ptr<DocumentModel> model = new DocumentModel;
			if (xml->rootElement->name.value == L"Doc")
			{
				FOREACH(Ptr<XmlElement>, partElement, XmlGetElements(xml->rootElement))
				{
					if (partElement->name.value == L"Styles")
					{
						FOREACH(Ptr<XmlElement>, styleElement, XmlGetElements(partElement))
						{
							if (styleElement->name.value == L"Style")
							{
								if (Ptr<XmlAttribute> name = XmlGetAttribute(styleElement, L"name"))
								{
									auto style = ParseDocumentStyle(resource, styleElement, errors);
									auto styleName = name->value.value;
									if (!model->styles.Keys().Contains(styleName))
									{
										model->styles.Add(styleName, style);
										if (styleName.Length() > 9 && styleName.Right(9) == L"-Override")
										{
											auto overridedStyle = MakePtr<DocumentStyle>();
											overridedStyle->styles = new DocumentStyleProperties;
											MergeStyle(overridedStyle->styles, style->styles);

											styleName = styleName.Left(styleName.Length() - 9);
											auto index = model->styles.Keys().IndexOf(styleName);
											if (index == -1)
											{
												model->styles.Add(styleName, overridedStyle);
											}
											else
											{
												auto originalStyle = model->styles.Values()[index];
												MergeStyle(overridedStyle->styles, originalStyle->styles);
												originalStyle->styles = overridedStyle->styles;
											}
										}
									}
								}
								else
								{
									errors.Add(GuiResourceError({ {resource},styleElement->codeRange.start }, L"Attribute \"name\" is missing in <Style>."));
								}
							}
							else
							{
								errors.Add(GuiResourceError({ {resource},styleElement->codeRange.start }, L"Unknown element in <Styles>: \"" + styleElement->name.value + L"\"."));
							}
						}
					}
					else if (partElement->name.value == L"Content")
					{
						FOREACH_INDEXER(Ptr<XmlElement>, p, i, XmlGetElements(partElement))
						{
							if (p->name.value == L"p")
							{
								Ptr<DocumentParagraphRun> paragraph = new DocumentParagraphRun;
								if (Ptr<XmlAttribute> att = XmlGetAttribute(p, L"align"))
								{
									if (att->value.value == L"Left")
									{
										paragraph->alignment = Alignment::Left;
									}
									else if (att->value.value == L"Center")
									{
										paragraph->alignment = Alignment::Center;
									}
									else if (att->value.value == L"Right")
									{
										paragraph->alignment = Alignment::Right;
									}
									else
									{
										errors.Add(GuiResourceError({ {resource},att->value.codeRange.start }, L"Unknown value in align attribute \"" + att->value.value + L"\"."));
									}
								}
								model->paragraphs.Add(paragraph);
								DeserializeNodeVisitor visitor(model, paragraph, i, resource, resolver, errors);
								p->Accept(&visitor);
							}
							else
							{
								errors.Add(GuiResourceError({ {resource},p->codeRange.start }, L"Unknown element in <Content>: \"" + p->name.value + L"\"."));
							}
						}
					}
					else
					{
						errors.Add(GuiResourceError({ {resource},partElement->codeRange.start }, L"Unknown element in <Doc>: \"" + partElement->name.value + L"\"."));
					}
				}
			}
			else
			{
				errors.Add(GuiResourceError({ {resource},xml->rootElement->codeRange.start }, L"The root element of document should be \"Doc\"."));
			}
			return model;
		}
	}
}

/***********************************************************************
.\RESOURCES\GUIPARSERMANAGER.CPP
***********************************************************************/

namespace vl
{
	namespace presentation
	{
		using namespace collections;
		using namespace controls;
		using namespace parsing::tabling;
		using namespace parsing::xml;
		using namespace parsing::json;
		using namespace regex;

/***********************************************************************
IGuiParserManager
***********************************************************************/

		IGuiParserManager* parserManager=0;

		IGuiParserManager* GetParserManager()
		{
			return parserManager;
		}

		class GuiParserManager : public Object, public IGuiParserManager, public IGuiPlugin
		{
		protected:
			Dictionary<WString, Ptr<Table>>				tables;
			Dictionary<WString, Func<Ptr<Table>()>>		loaders;
			SpinLock									lock;

			Dictionary<WString, Ptr<IGuiGeneralParser>>	parsers;
		public:
			
			GUI_PLUGIN_NAME(GacUI_Parser)
			{
			}

			void Load()override
			{
				parserManager=this;
				SetParsingTable(L"XML", &XmlLoadTable);
				SetParsingTable(L"JSON", &JsonLoadTable);
				SetTableParser(L"XML", L"XML", &XmlParseDocument);
				SetTableParser(L"JSON", L"JSON", &JsonParse);
			}

			void Unload()override
			{
				parserManager=0;
			}

			Ptr<Table> GetParsingTable(const WString& name)override
			{
				SPIN_LOCK(lock)
				{
					vint index=tables.Keys().IndexOf(name);
					if(index!=-1)
					{
						return tables.Values()[index];
					}

					index=loaders.Keys().IndexOf(name);
					if(index!=-1)
					{
						Ptr<Table> table=loaders.Values()[index]();
						tables.Add(name, table);
						return table;
					}
				}
				return 0;
			}

			bool SetParsingTable(const WString& name, Func<Ptr<Table>()> loader)override
			{
				if(loaders.Keys().Contains(name)) return false;
				loaders.Add(name, loader);
				return true;
			}

			Ptr<IGuiGeneralParser> GetParser(const WString& name)override
			{
				vint index=parsers.Keys().IndexOf(name);
				return index == -1 ? nullptr : parsers.Values()[index];
			}

			bool SetParser(const WString& name, Ptr<IGuiGeneralParser> parser)override
			{
				if(parsers.Keys().Contains(name)) return false;
				parsers.Add(name, parser);
				return true;
			}
		};
		GUI_REGISTER_PLUGIN(GuiParserManager)
	}
}

/***********************************************************************
.\RESOURCES\GUIRESOURCE.CPP
***********************************************************************/

namespace vl
{
	namespace presentation
	{
		using namespace controls;
		using namespace collections;
		using namespace parsing;
		using namespace parsing::tabling;
		using namespace parsing::xml;
		using namespace parsing::json;
		using namespace regex;
		using namespace stream;
		using namespace filesystem;

		WString GetFolderPath(const WString& filePath)
		{
			auto path = FilePath(filePath).GetFolder().GetFullPath();
			if (path != L"")
			{
				if (path[path.Length() - 1] != FilePath::Delimiter)
				{
					path += FilePath::Delimiter;
				}
			}
			return path;
		}

		WString GetFileName(const WString& filePath)
		{
			return FilePath(filePath).GetName();
		}

		bool LoadTextFile(const WString& filePath, WString& text)
		{
			BomEncoder::Encoding encoding;
			bool bom;
			return File(filePath).ReadAllTextWithEncodingTesting(text, encoding, bom);
		}

		bool IsResourceUrl(const WString& text, WString& protocol, WString& path)
		{
			Pair<vint, vint> index = INVLOC.FindFirst(text, L"://", Locale::None);
			if (index.key != -1)
			{
				protocol = INVLOC.ToLower(text.Sub(0, index.key));
				path = text.Sub(index.key + index.value, text.Length() - index.key - index.value);
				return true;
			}
			else
			{
				return false;
			}
		}

		vint HexToInt(wchar_t c)
		{
			if (L'0' <= c && c <= L'9')
			{
				return c - L'0';
			}
			else if (L'A' <= c && c <= L'F')
			{
				return c - L'A' + 10;
			}
			else
			{
				return 0;
			}
		}

		void HexToBinary(stream::IStream& stream, const WString& hexText)
		{
			const wchar_t* buffer = hexText.Buffer();
			vint count = hexText.Length() / 2;
			for (vint i = 0; i < count; i++)
			{
				vuint8_t byte = (vuint8_t)(HexToInt(buffer[0]) * 16 + HexToInt(buffer[1]));
				buffer += 2;
				stream.Write(&byte, 1);
			}
		}

		WString BinaryToHex(stream::IStream& stream)
		{
			stream::MemoryStream memoryStream;
			{
				stream::StreamWriter writer(memoryStream);
				vuint8_t byte;
				while (stream.Read(&byte, 1) == 1)
				{
					writer.WriteChar(L"0123456789ABCDEF"[byte / 16]);
					writer.WriteChar(L"0123456789ABCDEF"[byte % 16]);
				}
			}
			memoryStream.SeekFromBegin(0);
			{
				stream::StreamReader reader(memoryStream);
				return reader.ReadToEnd();
			}
		}

/***********************************************************************
GlobalStringKey
***********************************************************************/

		GlobalStringKey GlobalStringKey::Empty;
		GlobalStringKey GlobalStringKey::_InferType;
		GlobalStringKey GlobalStringKey::_Set;
		GlobalStringKey GlobalStringKey::_Ref;
		GlobalStringKey GlobalStringKey::_Bind;
		GlobalStringKey GlobalStringKey::_Format;
		GlobalStringKey GlobalStringKey::_Str;
		GlobalStringKey GlobalStringKey::_Eval;
		GlobalStringKey GlobalStringKey::_Uri;
		GlobalStringKey GlobalStringKey::_ControlTemplate;

		class GlobalStringKeyManager
		{
		public:
			Dictionary<WString, vint>		stoi;
			List<WString>					itos;

			void InitializeConstants()
			{
				GlobalStringKey::_Set = GlobalStringKey::Get(L"set");
				GlobalStringKey::_InferType = GlobalStringKey::Get(L"_");
				GlobalStringKey::_Ref = GlobalStringKey::Get(L"ref");
				GlobalStringKey::_Bind = GlobalStringKey::Get(L"bind");
				GlobalStringKey::_Format = GlobalStringKey::Get(L"format");
				GlobalStringKey::_Str = GlobalStringKey::Get(L"str");
				GlobalStringKey::_Eval = GlobalStringKey::Get(L"eval");
				GlobalStringKey::_Uri = GlobalStringKey::Get(L"uri");
				GlobalStringKey::_ControlTemplate = GlobalStringKey::Get(L"ControlTemplate");
			}
		}*globalStringKeyManager = 0;

		GlobalStringKey GlobalStringKey::Get(const WString& string)
		{
			GlobalStringKey key;
			if (string != L"")
			{
				vint index = globalStringKeyManager->stoi.Keys().IndexOf(string);
				if (index == -1)
				{
					key.key = globalStringKeyManager->itos.Add(string);
					globalStringKeyManager->stoi.Add(string, key.key);
				}
				else
				{
					key.key = globalStringKeyManager->stoi.Values()[index];
				}
			}
			return key;
		}

		vint GlobalStringKey::ToKey()const
		{
			return key;
		}

		WString GlobalStringKey::ToString()const
		{
			return *this == GlobalStringKey::Empty
				? L""
				: globalStringKeyManager->itos[key];
		}

/***********************************************************************
GuiImageData
***********************************************************************/

		GuiImageData::GuiImageData()
			:frameIndex(-1)
		{
		}

		GuiImageData::GuiImageData(Ptr<INativeImage> _image, vint _frameIndex)
			: image(_image)
			, frameIndex(_frameIndex)
		{
		}

		GuiImageData::~GuiImageData()
		{
		}

		Ptr<INativeImage> GuiImageData::GetImage()
		{
			return image;
		}

		vint GuiImageData::GetFrameIndex()
		{
			return frameIndex;
		}

/***********************************************************************
GuiTextData
***********************************************************************/

		GuiTextData::GuiTextData()
		{
		}

		GuiTextData::GuiTextData(const WString& _text)
			:text(_text)
		{
		}

		WString GuiTextData::GetText()
		{
			return text;
		}

/***********************************************************************
GuiResourceNodeBase
***********************************************************************/

		GuiResourceNodeBase::GuiResourceNodeBase()
			:parent(0)
		{
		}

		GuiResourceNodeBase::~GuiResourceNodeBase()
		{
		}

		const WString& GuiResourceNodeBase::GetName()
		{
			return name;
		}

		WString GuiResourceNodeBase::GetResourcePath()
		{
			auto resourcePath = name;
			auto current = parent;
			while (current && current->GetParent())
			{
				resourcePath = current->GetName() + L"/" + resourcePath;
				current = current->GetParent();
			}
			return resourcePath;
		}

		const WString& GuiResourceNodeBase::GetFileContentPath()
		{
			return fileContentPath;
		}

		const WString& GuiResourceNodeBase::GetFileAbsolutePath()
		{
			return fileAbsolutePath;
		}

		void GuiResourceNodeBase::SetFileContentPath(const WString& content, const WString& absolute)
		{
			fileContentPath = content;
			fileAbsolutePath = absolute;
		}

		GuiResourceFolder* GuiResourceNodeBase::GetParent()
		{
			return parent;
		}

/***********************************************************************
GuiResourceLocation
***********************************************************************/

		GuiResourceLocation::GuiResourceLocation(const WString& _resourcePath, const WString& _filePath)
			:resourcePath(_resourcePath)
			, filePath(_filePath)
		{
		}

		GuiResourceLocation::GuiResourceLocation(Ptr<GuiResourceNodeBase> node)
		{
			if (node)
			{
				resourcePath = node->GetResourcePath();

				auto current = node.Obj();
				while (current)
				{
					if (current->GetFileContentPath() != L"")
					{
						filePath = current->GetFileAbsolutePath();
						break;
					}
					current = current->GetParent();
				}
			}
		}

/***********************************************************************
GuiResourceTextPos
***********************************************************************/

		GuiResourceTextPos::GuiResourceTextPos(GuiResourceLocation location, parsing::ParsingTextPos position)
			:originalLocation(location)
			, row(position.row)
			, column(position.column)
		{
		}

/***********************************************************************
GuiResourceError
***********************************************************************/

		GuiResourceError::GuiResourceError(GuiResourceTextPos _position, const WString& _message)
			:location(_position.originalLocation)
			, position(_position)
			, message(_message)
		{
		}

		GuiResourceError::GuiResourceError(GuiResourceLocation _location, const WString& _message)
			:location(_location)
			, position(_location, {})
			, message(_message)
		{
		}

		GuiResourceError::GuiResourceError(GuiResourceLocation _location, GuiResourceTextPos _position, const WString& _message)
			:location(_location)
			, position(_position)
			, message(_message)
		{
		}

		template<typename TCallback>
		void TransformErrors(GuiResourceError::List& errors, collections::List<Ptr<parsing::ParsingError>>& parsingErrors, GuiResourceTextPos offset, const TCallback& callback)
		{
			if (offset.row < 0 || offset.column < 0)
			{
				offset.row = 0;
				offset.column = 0;
			}

			FOREACH(Ptr<ParsingError>, error, parsingErrors)
			{
				auto pos = error->codeRange.start;
				if (pos.row < 0 || pos.column < 0)
				{
					pos = { offset.row,offset.column };
				}
				else
				{
					if (pos.row == 0)
					{
						pos.column += offset.column;
					}
					pos.row += offset.row;
				}
				errors.Add(callback({ offset.originalLocation,pos }, error->errorMessage));
			}
		}

		void GuiResourceError::Transform(GuiResourceLocation _location, GuiResourceError::List& errors, collections::List<Ptr<parsing::ParsingError>>& parsingErrors)
		{
			Transform(_location, errors, parsingErrors, { _location,{ 0,0 } });
		}

		void GuiResourceError::Transform(GuiResourceLocation _location, GuiResourceError::List& errors, collections::List<Ptr<parsing::ParsingError>>& parsingErrors, parsing::ParsingTextPos offset)
		{
			Transform(_location, errors, parsingErrors, { _location,offset });
		}

		void GuiResourceError::Transform(GuiResourceLocation _location, GuiResourceError::List& errors, collections::List<Ptr<parsing::ParsingError>>& parsingErrors, GuiResourceTextPos offset)
		{
			TransformErrors(errors, parsingErrors, offset, [&](GuiResourceTextPos pos, const WString& message)
			{
				return GuiResourceError(_location, pos, message);
			});
		}

		void GuiResourceError::SortAndLog(List& errors, collections::List<WString>& output, const WString& workingDirectory)
		{
			if (errors.Count() == 0) return;
			SortLambda(&errors[0], errors.Count(), [](const GuiResourceError& a, const GuiResourceError& b)
			{
				vint result = 0;
				if (result == 0) result = WString::Compare(a.location.resourcePath, b.location.resourcePath);
				if (result == 0) result = WString::Compare(a.location.filePath, b.location.filePath);
				if (result == 0) result = WString::Compare(a.position.originalLocation.resourcePath, b.position.originalLocation.resourcePath);
				if (result == 0) result = WString::Compare(a.position.originalLocation.filePath, b.position.originalLocation.filePath);
				if (result == 0) result = a.position.row - b.position.row;
				if (result == 0) result = a.position.column - b.position.column;
				return result;
			});

			FOREACH_INDEXER(GuiResourceError, error, index, errors)
			{
				bool needHeader = index == 0;
				if (index > 0)
				{
					auto previousError = errors[index - 1];
					if (error.location != previousError.location || error.position.originalLocation != previousError.position.originalLocation)
					{
						needHeader = true;
					}
				}

#define CONVERT_FILEPATH(FILEPATH) (workingDirectory == L"" ? FILEPATH  : filesystem::FilePath(workingDirectory).GetRelativePathFor(FILEPATH))
#define CONVERT_LOCATION(LOCATION) (LOCATION).resourcePath + L" # " + CONVERT_FILEPATH((LOCATION).filePath)
				if (needHeader)
				{
					output.Add(CONVERT_LOCATION(error.location));
					if (error.location != error.position.originalLocation)
					{
						output.Add(L"    Original: " + CONVERT_LOCATION(error.position.originalLocation));
					}
				}

				WString prefix = L"Failed to load file \"";
				WString postfix = L"\".";
				if (INVLOC.StartsWith(error.message, prefix, Locale::Normalization::None) && INVLOC.EndsWith(error.message, postfix, Locale::Normalization::None))
				{
					auto path = error.message.Sub(prefix.Length(), error.message.Length() - prefix.Length() - postfix.Length());
					path = CONVERT_FILEPATH(path);
					error.message = prefix + path + postfix;
				}

				auto row = error.position.row;
				if (row >= 0) row++;
				auto column = error.position.column;
				if (column >= 0) column++;
				output.Add(L"(" + itow(row) + L", " + itow(column) + L"): " + error.message);
#undef CONVERT_FILEPATH
#undef CONVERT_LOCATION
			}
		}

/***********************************************************************
GuiResourceItem
***********************************************************************/

		GuiResourceItem::GuiResourceItem()
		{
		}

		GuiResourceItem::~GuiResourceItem()
		{
		}

		const WString& GuiResourceItem::GetTypeName()
		{
			return typeName;
		}

		Ptr<DescriptableObject> GuiResourceItem::GetContent()
		{
			return content;
		}

		void GuiResourceItem::SetContent(const WString& _typeName, Ptr<DescriptableObject> value)
		{
			typeName = _typeName;
			content = value;
		}

		Ptr<GuiImageData> GuiResourceItem::AsImage()
		{
			return content.Cast<GuiImageData>();
		}

		Ptr<parsing::xml::XmlDocument> GuiResourceItem::AsXml()
		{
			return content.Cast<XmlDocument>();
		}

		Ptr<GuiTextData> GuiResourceItem::AsString()
		{
			return content.Cast<GuiTextData>();
		}

		Ptr<DocumentModel> GuiResourceItem::AsDocument()
		{
			return content.Cast<DocumentModel>();
		}

/***********************************************************************
GuiResourceFolder
***********************************************************************/

		void GuiResourceFolder::LoadResourceFolderFromXml(DelayLoadingList& delayLoadings, const WString& containingFolder, Ptr<parsing::xml::XmlElement> folderXml, GuiResourceError::List& errors)
		{
			ClearItems();
			ClearFolders();
			FOREACH(Ptr<XmlElement>, element, XmlGetElements(folderXml))
			{
				WString name;
				if (Ptr<XmlAttribute> nameAtt = XmlGetAttribute(element, L"name"))
				{
					name = nameAtt->value.value;
				}
				if (element->name.value == L"Folder")
				{
					if (name == L"")
					{
						errors.Add(GuiResourceError({ {this},element->codeRange.start }, L"A resource folder should have a name."));
					}
					else
					{
						Ptr<GuiResourceFolder> folder = new GuiResourceFolder;
						if (AddFolder(name, folder))
						{
							WString newContainingFolder = containingFolder;
							Ptr<XmlElement> newFolderXml = element;
							if (Ptr<XmlAttribute> contentAtt = XmlGetAttribute(element, L"content"))
							{
								if (contentAtt->value.value == L"Link")
								{
									auto fileContentPath = XmlGetValue(element);
									auto fileAbsolutePath = containingFolder + fileContentPath;
									folder->SetFileContentPath(fileContentPath, fileAbsolutePath);

									WString text;
									if (LoadTextFile(fileAbsolutePath, text))
									{
										if (auto parser = GetParserManager()->GetParser<XmlDocument>(L"XML"))
										{
											if (auto xml = parser->Parse({ WString::Empty,fileAbsolutePath }, text, errors))
											{
												newContainingFolder = GetFolderPath(fileAbsolutePath);
												newFolderXml = xml->rootElement;
											}
										}
									}
									else
									{
										errors.Add(GuiResourceError({ {this},element->codeRange.start }, L"Failed to load file \"" + fileAbsolutePath + L"\"."));
									}
								}
							}
							folder->LoadResourceFolderFromXml(delayLoadings, newContainingFolder, newFolderXml, errors);
						}
						else
						{
							errors.Add(GuiResourceError({ {this},element->codeRange.start }, L"Duplicated resource folder name \"" + name + L"\"."));
						}
					}
				}
				else if (element->name.value.Length() <= 3 || element->name.value.Sub(0, 4) != L"ref.")
				{
					WString fileContentPath;
					WString fileAbsolutePath;
					if (Ptr<XmlAttribute> contentAtt = XmlGetAttribute(element, L"content"))
					{
						if (contentAtt->value.value == L"File")
						{
							fileContentPath = XmlGetValue(element);
							fileAbsolutePath = containingFolder + fileContentPath;
							if (name == L"")
							{
								name = GetFileName(fileAbsolutePath);
							}
						}
					}

					Ptr<GuiResourceItem> item = new GuiResourceItem;
					if (AddItem(name, item))
					{
						WString type = element->name.value;
						IGuiResourceTypeResolver* typeResolver = GetResourceResolverManager()->GetTypeResolver(type);
						IGuiResourceTypeResolver* preloadResolver = typeResolver;

						if (typeResolver)
						{
							if (!typeResolver->DirectLoadXml())
							{
								WString preloadType = typeResolver->IndirectLoad()->GetPreloadType();
								preloadResolver = GetResourceResolverManager()->GetTypeResolver(preloadType);
								if (!preloadResolver)
								{
									errors.Add(GuiResourceError({ {this}, element->codeRange.start }, L"[INTERNAL-ERROR] Unknown resource resolver \"" + preloadType + L"\" of resource type \"" + type + L"\"."));
								}
							}
						}
						else
						{
							errors.Add(GuiResourceError({ {this}, element->codeRange.start }, L"Unknown resource type \"" + type + L"\"."));
						}

						if (typeResolver && preloadResolver)
						{
							if (auto directLoad = preloadResolver->DirectLoadXml())
							{
								{
									Ptr<DescriptableObject> resource;
									if (fileAbsolutePath == L"")
									{
										resource = directLoad->ResolveResource(item, element, errors);
									}
									else
									{
										item->SetFileContentPath(fileContentPath, fileAbsolutePath);
										resource = directLoad->ResolveResource(item, fileAbsolutePath, errors);
									}
									item->SetContent(preloadResolver->GetType(), resource);
								}

								if (typeResolver != preloadResolver)
								{
									if (auto indirectLoad = typeResolver->IndirectLoad())
									{
										if (indirectLoad->IsDelayLoad())
										{
											DelayLoading delayLoading;
											delayLoading.type = type;
											delayLoading.workingDirectory = containingFolder;
											delayLoading.preloadResource = item;
											delayLoadings.Add(delayLoading);
										}
										else if (item->GetContent())
										{
											auto resource = indirectLoad->ResolveResource(item, 0, errors);
											item->SetContent(typeResolver->GetType(), resource);
										}
									}
									else
									{
										item->SetContent(typeResolver->GetType(), nullptr);
										errors.Add(GuiResourceError({ {this},element->codeRange.start }, L"[INTERNAL-ERROR] Resource type \"" + typeResolver->GetType() + L"\" is not a indirect load resource type."));									}
								}
							}
							else
							{
								errors.Add(GuiResourceError({ {this},element->codeRange.start }, L"[INTERNAL-ERROR] Resource type \"" + preloadResolver->GetType() + L"\" is not a direct load resource type."));
							}
						}

						if (!item->GetContent())
						{
							RemoveItem(name);
						}
					}
					else
					{
						errors.Add(GuiResourceError({ {this},element->codeRange.start }, L"Duplicated resource item name \"" + name + L"\"."));
					}
				}
			}
		}

		void GuiResourceFolder::SaveResourceFolderToXml(Ptr<parsing::xml::XmlElement> xmlParent)
		{
			FOREACH(Ptr<GuiResourceItem>, item, items.Values())
			{
				auto resolver = GetResourceResolverManager()->GetTypeResolver(item->GetTypeName());
				if (resolver->XmlSerializable())
				{
					auto attName = MakePtr<XmlAttribute>();
					attName->name.value = L"name";
					attName->value.value = item->GetName();

					if (item->GetFileContentPath() == L"")
					{
						Ptr<XmlElement> xmlElement;

						if (auto directLoad = resolver->DirectLoadXml())
						{
							xmlElement = directLoad->Serialize(item, item->GetContent());
						}
						else if (auto indirectLoad = resolver->IndirectLoad())
						{
							if (auto preloadResolver = GetResourceResolverManager()->GetTypeResolver(indirectLoad->GetPreloadType()))
							{
								if (auto directLoad = preloadResolver->DirectLoadXml())
								{
									if (auto resource = indirectLoad->Serialize(item, item->GetContent()))
									{
										xmlElement = directLoad->Serialize(item, resource);
										xmlElement->name.value = resolver->GetType();
									}
								}
							}
						}

						if (xmlElement)
						{
							xmlElement->attributes.Add(attName);
							xmlParent->subNodes.Add(xmlElement);
						}
					}
					else
					{
						auto xmlElement = MakePtr<XmlElement>();
						xmlElement->name.value = item->GetTypeName();
						xmlParent->subNodes.Add(xmlElement);

						auto attContent = MakePtr<XmlAttribute>();
						attContent->name.value = L"content";
						attContent->value.value = L"File";
						xmlElement->attributes.Add(attContent);

						auto xmlText = MakePtr<XmlText>();
						xmlText->content.value = item->GetFileContentPath();
						xmlElement->subNodes.Add(xmlText);
					}
				}
			}

			FOREACH(Ptr<GuiResourceFolder>, folder, folders.Values())
			{
				auto attName = MakePtr<XmlAttribute>();
				attName->name.value = L"name";
				attName->value.value = folder->GetName();

				auto xmlFolder = MakePtr<XmlElement>();
				xmlFolder->name.value = L"Folder";
				xmlFolder->attributes.Add(attName);
				xmlParent->subNodes.Add(xmlFolder);
				

				if (folder->GetFileContentPath() == L"")
				{
					folder->SaveResourceFolderToXml(xmlFolder);
				}
				else
				{
					auto attContent = MakePtr<XmlAttribute>();
					attContent->name.value = L"content";
					attContent->value.value = L"Link";
					xmlFolder->attributes.Add(attContent);

					auto xmlText = MakePtr<XmlText>();
					xmlText->content.value = folder->GetFileContentPath();
					xmlFolder->subNodes.Add(xmlText);
				}
			}
		}

		void GuiResourceFolder::CollectTypeNames(collections::List<WString>& typeNames)
		{
			FOREACH(Ptr<GuiResourceItem>, item, items.Values())
			{
				if (!typeNames.Contains(item->GetTypeName()))
				{
					typeNames.Add(item->GetTypeName());
				}
			}
			FOREACH(Ptr<GuiResourceFolder>, folder, folders.Values())
			{
				folder->CollectTypeNames(typeNames);
			}
		}

		void GuiResourceFolder::LoadResourceFolderFromBinary(DelayLoadingList& delayLoadings, stream::internal::ContextFreeReader& reader, collections::List<WString>& typeNames, GuiResourceError::List& errors)
		{
			vint count = 0;
			reader << count;
			for (vint i = 0; i < count; i++)
			{
				vint typeName = 0;
				WString name;
				reader << typeName << name;

				auto resolver = GetResourceResolverManager()->GetTypeResolver(typeNames[typeName]);
				Ptr<GuiResourceItem> item = new GuiResourceItem;
				if(AddItem(name, item))
				{
					WString type = typeNames[typeName];
					IGuiResourceTypeResolver* typeResolver = GetResourceResolverManager()->GetTypeResolver(type);
					IGuiResourceTypeResolver* preloadResolver = typeResolver;

					if(typeResolver)
					{
						if (!typeResolver->DirectLoadStream())
						{
							WString preloadType = typeResolver->IndirectLoad()->GetPreloadType();
							if (preloadType != L"")
							{
								preloadResolver = GetResourceResolverManager()->GetTypeResolver(preloadType);
								if (!preloadResolver)
								{
									errors.Add(GuiResourceError({ item }, L"[INTERNAL-ERROR] Unknown resource resolver \"" + preloadType + L"\" of resource type \"" + type + L"\"."));
								}
							}
						}
					}
					else
					{
						errors.Add(GuiResourceError({ item }, L"[BINARY] Unknown resource type \"" + type + L"\"."));
					}

					if(typeResolver && preloadResolver)
					{
						if (auto directLoad = preloadResolver->DirectLoadStream())
						{
							{
								auto resource = directLoad->ResolveResourcePrecompiled(item, reader.input, errors);
								item->SetContent(preloadResolver->GetType(), resource);
							}

							if (typeResolver != preloadResolver)
							{
								if (auto indirectLoad = typeResolver->IndirectLoad())
								{
									if(indirectLoad->IsDelayLoad())
									{
										DelayLoading delayLoading;
										delayLoading.type = type;
										delayLoading.preloadResource = item;
										delayLoadings.Add(delayLoading);
									}
									else if(item->GetContent())
									{
										auto resource = indirectLoad->ResolveResource(item, nullptr, errors);
										item->SetContent(typeResolver->GetType(), resource);
									}
								}
								else
								{
									item->SetContent(typeResolver->GetType(), nullptr);
									errors.Add(GuiResourceError({ item }, L"[INTERNAL-ERROR] Resource type \"" + typeResolver->GetType() + L"\" is not a indirect load resource type."));
								}
							}
						}
						else
						{
							errors.Add(GuiResourceError({ item }, L"[INTERNAL-ERROR] Resource type \"" + preloadResolver->GetType() + L"\" is not a direct load resource type."));
						}
					}

					if(!item->GetContent())
					{
						RemoveItem(name);
					}
				}
				else
				{
					errors.Add(GuiResourceError({ this }, L"[BINARY] Duplicated resource item name \"" + name + L"\"."));
				}
			}

			reader << count;
			for (vint i = 0; i < count; i++)
			{
				WString name;
				reader << name;

				auto folder = MakePtr<GuiResourceFolder>();
				folder->LoadResourceFolderFromBinary(delayLoadings, reader, typeNames, errors);
				AddFolder(name, folder);
			}
		}

		void GuiResourceFolder::SaveResourceFolderToBinary(stream::internal::ContextFreeWriter& writer, collections::List<WString>& typeNames)
		{
			typedef Tuple<vint, WString, IGuiResourceTypeResolver_DirectLoadStream*, Ptr<GuiResourceItem>, Ptr<DescriptableObject>> ItemTuple;
			List<ItemTuple> itemTuples;

			FOREACH(Ptr<GuiResourceItem>, item, items.Values())
			{
				auto resolver = GetResourceResolverManager()->GetTypeResolver(item->GetTypeName());
				if (resolver->StreamSerializable())
				{
					vint typeName = typeNames.IndexOf(item->GetTypeName());
					WString name = item->GetName();
				
					if (auto directLoad = resolver->DirectLoadStream())
					{
						itemTuples.Add(ItemTuple(typeName, name, directLoad, item, item->GetContent()));
					}
					else if (auto indirectLoad = resolver->IndirectLoad())
					{
						if (auto preloadResolver = GetResourceResolverManager()->GetTypeResolver(indirectLoad->GetPreloadType()))
						{
							if (auto directLoad = preloadResolver->DirectLoadStream())
							{
								if (auto resource = indirectLoad->Serialize(item, item->GetContent()))
								{
									itemTuples.Add(ItemTuple(typeName, name, directLoad, item, resource));
								}
							}
						}
					}
				}
			}

			vint count = itemTuples.Count();
			writer << count;
			FOREACH(ItemTuple, item, itemTuples)
			{
				vint typeName = item.f0;
				WString name = item.f1;
				writer << typeName << name;

				auto directLoad = item.f2;
				auto resource = item.f3;
				auto content = item.f4;
				directLoad->SerializePrecompiled(resource, content, writer.output);
			}

			count = folders.Count();
			writer << count;
			FOREACH(Ptr<GuiResourceFolder>, folder, folders.Values())
			{
				WString name = folder->GetName();
				writer << name;
				folder->SaveResourceFolderToBinary(writer, typeNames);
			}
		}

		void GuiResourceFolder::PrecompileResourceFolder(GuiResourcePrecompileContext& context, IGuiResourcePrecompileCallback* callback, GuiResourceError::List& errors)
		{
			FOREACH(Ptr<GuiResourceItem>, item, items.Values())
			{
				auto typeResolver = GetResourceResolverManager()->GetTypeResolver(item->GetTypeName());
				if (auto precompile = typeResolver->Precompile())
				{
					if (precompile->GetPassSupport(context.passIndex) == IGuiResourceTypeResolver_Precompile::PerResource)
					{
						if (callback)
						{
							callback->OnPerResource(context.passIndex, item);
						}
						precompile->PerResourcePrecompile(item, context, errors);
					}
				}
			}

			FOREACH(Ptr<GuiResourceFolder>, folder, folders.Values())
			{
				folder->PrecompileResourceFolder(context, callback, errors);
			}
		}

		void GuiResourceFolder::InitializeResourceFolder(GuiResourceInitializeContext& context)
		{
			FOREACH(Ptr<GuiResourceItem>, item, items.Values())
			{
				auto typeResolver = GetResourceResolverManager()->GetTypeResolver(item->GetTypeName());
				if (auto initialize = typeResolver->Initialize())
				{
					initialize->Initialize(item, context);
				}
			}

			FOREACH(Ptr<GuiResourceFolder>, folder, folders.Values())
			{
				folder->InitializeResourceFolder(context);
			}
		}

		GuiResourceFolder::GuiResourceFolder()
		{
		}

		GuiResourceFolder::~GuiResourceFolder()
		{
		}

		const GuiResourceFolder::ItemList& GuiResourceFolder::GetItems()
		{
			return items.Values();
		}

		Ptr<GuiResourceItem> GuiResourceFolder::GetItem(const WString& name)
		{
			vint index=items.Keys().IndexOf(name);
			return index == -1 ? nullptr : items.Values().Get(index);
		}

		bool GuiResourceFolder::AddItem(const WString& name, Ptr<GuiResourceItem> item)
		{
			if (item->GetParent() != 0 || items.Keys().Contains(name)) return false;
			items.Add(name, item);
			item->parent = this;
			item->name = name;
			return true;
		}

		Ptr<GuiResourceItem> GuiResourceFolder::RemoveItem(const WString& name)
		{
			Ptr<GuiResourceItem> item = GetItem(name);
			if (!item) return 0;
			items.Remove(name);
			item->parent = nullptr;
			item->name = L"";
			return item;
		}

		void GuiResourceFolder::ClearItems()
		{
			items.Clear();
		}

		const GuiResourceFolder::FolderList& GuiResourceFolder::GetFolders()
		{
			return folders.Values();
		}

		Ptr<GuiResourceFolder> GuiResourceFolder::GetFolder(const WString& name)
		{
			vint index=folders.Keys().IndexOf(name);
			return index == -1 ? nullptr : folders.Values().Get(index);
		}

		bool GuiResourceFolder::AddFolder(const WString& name, Ptr<GuiResourceFolder> folder)
		{
			if (folder->GetParent() != 0 || folders.Keys().Contains(name)) return false;
			folders.Add(name, folder);
			folder->parent = this;
			folder->name = name;
			return true;
		}

		Ptr<GuiResourceFolder> GuiResourceFolder::RemoveFolder(const WString& name)
		{
			Ptr<GuiResourceFolder> folder = GetFolder(name);
			if (!folder) return 0;
			folders.Remove(name);
			folder->parent = nullptr;
			folder->name = L"";
			return folder;
		}

		void GuiResourceFolder::ClearFolders()
		{
			folders.Clear();
		}

		Ptr<DescriptableObject> GuiResourceFolder::GetValueByPath(const WString& path)
		{
			const wchar_t* buffer=path.Buffer();
			const wchar_t* index=wcschr(buffer, L'\\');
			if(!index) index=wcschr(buffer, '/');

			if(index)
			{
				WString name=path.Sub(0, index-buffer);
				Ptr<GuiResourceFolder> folder=GetFolder(name);
				if(folder)
				{
					vint start=index-buffer+1;
					return folder->GetValueByPath(path.Sub(start, path.Length()-start));
				}
			}
			else
			{
				Ptr<GuiResourceItem> item=GetItem(path);
				if(item)
				{
					return item->GetContent();
				}
			}
			return 0;
		}

		Ptr<GuiResourceFolder> GuiResourceFolder::GetFolderByPath(const WString& path)
		{
			const wchar_t* buffer=path.Buffer();
			const wchar_t* index=wcschr(buffer, L'\\');
			if(!index) index=wcschr(buffer, '/');
			if(!index) return 0;

			WString name=path.Sub(0, index-buffer);
			Ptr<GuiResourceFolder> folder=GetFolder(name);

			if(index-buffer==path.Length()-1)
			{
				return folder;
			}

			if(folder)
			{
				vint start=index-buffer+1;
				return folder->GetFolderByPath(path.Sub(start, path.Length()-start));
			}

			return 0;
		}

		bool GuiResourceFolder::CreateValueByPath(const WString& path, const WString& typeName, Ptr<DescriptableObject> value)
		{
			const wchar_t* buffer = path.Buffer();
			const wchar_t* index = wcschr(buffer, L'\\');
			if (!index) index = wcschr(buffer, '/');

			if(index)
			{
				WString name = path.Sub(0, index - buffer);
				Ptr<GuiResourceFolder> folder = GetFolder(name);
				if (!folder)
				{
					folder = new GuiResourceFolder;
					AddFolder(name, folder);
				}
				vint start = index - buffer + 1;
				return folder->CreateValueByPath(path.Sub(start, path.Length() - start), typeName, value);
			}
			else
			{
				if(GetItem(path))
				{
					return false;
				}

				auto item = new GuiResourceItem;
				item->SetContent(typeName, value);
				return AddItem(path, item);
			}
		}

/***********************************************************************
GuiResource
***********************************************************************/

		void GuiResource::ProcessDelayLoading(Ptr<GuiResource> resource, DelayLoadingList& delayLoadings, GuiResourceError::List& errors)
		{
			FOREACH(DelayLoading, delay, delayLoadings)
			{
				WString type = delay.type;
				WString folder = delay.workingDirectory;
				Ptr<GuiResourceItem> item = delay.preloadResource;

				if (auto typeResolver = GetResourceResolverManager()->GetTypeResolver(type))
				{
					if (auto indirectLoad = typeResolver->IndirectLoad())
					{
						if (item->GetContent())
						{
							Ptr<GuiResourcePathResolver> pathResolver = new GuiResourcePathResolver(resource, folder);
							Ptr<DescriptableObject> resource = indirectLoad->ResolveResource(item, pathResolver, errors);
							if (resource)
							{
								item->SetContent(typeResolver->GetType(), resource);
							}
						}
					}
					else
					{
						errors.Add(GuiResourceError({ item }, L"[INTERNAL-ERROR] Resource type \"" + type + L"\" is not a indirect load resource type."));
					}
				}
				else
				{
					errors.Add(GuiResourceError({ item }, L"[INTERNAL-ERROR] Unknown resource type \"" + type + L"\"."));
				}
			}
		}

		GuiResource::GuiResource()
		{
		}

		GuiResource::~GuiResource()
		{
		}

		WString GuiResource::GetWorkingDirectory()
		{
			return workingDirectory;
		}

		Ptr<GuiResource> GuiResource::LoadFromXml(Ptr<parsing::xml::XmlDocument> xml, const WString& filePath, const WString& workingDirectory, GuiResourceError::List& errors)
		{
			Ptr<GuiResource> resource = new GuiResource;
			resource->SetFileContentPath(filePath, filePath);
			resource->workingDirectory = workingDirectory;
			DelayLoadingList delayLoadings;
			resource->LoadResourceFolderFromXml(delayLoadings, resource->workingDirectory, xml->rootElement, errors);

			ProcessDelayLoading(resource, delayLoadings, errors);
			return resource;
		}

		Ptr<GuiResource> GuiResource::LoadFromXml(const WString& filePath, GuiResourceError::List& errors)
		{
			Ptr<XmlDocument> xml;
			if(auto parser=GetParserManager()->GetParser<XmlDocument>(L"XML"))
			{
				WString text;
				if(LoadTextFile(filePath, text))
				{
					xml = parser->Parse({ WString::Empty,filePath }, text, errors);
				}
				else
				{
					errors.Add(GuiResourceError({ WString::Empty,filePath }, L"Failed to load file \"" + filePath + L"\"."));
				}
			}
			if(xml)
			{
				return LoadFromXml(xml, filePath, GetFolderPath(filePath), errors);
			}
			return 0;
		}

		Ptr<parsing::xml::XmlDocument> GuiResource::SaveToXml()
		{
			auto xmlRoot = MakePtr<XmlElement>();
			xmlRoot->name.value = L"Resource";
			SaveResourceFolderToXml(xmlRoot);

			auto doc = MakePtr<XmlDocument>();
			doc->rootElement = xmlRoot;
			return doc;
		}

		Ptr<GuiResource> GuiResource::LoadPrecompiledBinary(stream::IStream& stream, GuiResourceError::List& errors)
		{
			stream::internal::ContextFreeReader reader(stream);
			auto resource = MakePtr<GuiResource>();

			List<WString> typeNames;
			reader << typeNames;
			
			DelayLoadingList delayLoadings;
			resource->LoadResourceFolderFromBinary(delayLoadings, reader, typeNames, errors);
			
			ProcessDelayLoading(resource, delayLoadings, errors);
			return resource;
		}

		Ptr<GuiResource> GuiResource::LoadPrecompiledBinary(stream::IStream& stream)
		{
			GuiResourceError::List errors;
			auto resource = LoadPrecompiledBinary(stream, errors);
			CHECK_ERROR(errors.Count() == 0, L"GuiResource::LoadPrecompiledBinary(IStream&)#There are errors.");
			return resource;
		}

		void GuiResource::SavePrecompiledBinary(stream::IStream& stream)
		{
			stream::internal::ContextFreeWriter writer(stream);

			List<WString> typeNames;
			CollectTypeNames(typeNames);
			writer << typeNames;
			SaveResourceFolderToBinary(writer, typeNames);
		}

		Ptr<GuiResourceFolder> GuiResource::Precompile(IGuiResourcePrecompileCallback* callback, GuiResourceError::List& errors)
		{
			if (GetFolder(L"Precompiled"))
			{
				errors.Add(GuiResourceError({ this }, L"A precompiled resource cannot be compiled again."));
				return nullptr;
			}

			GuiResourcePrecompileContext context;
			context.compilerCallback = callback ? callback->GetCompilerCallback() : nullptr;
			context.rootResource = this;
			context.resolver = new GuiResourcePathResolver(this, workingDirectory);
			context.targetFolder = new GuiResourceFolder;
			
			auto manager = GetResourceResolverManager();
			vint maxPass = manager->GetMaxPrecompilePassIndex();
			List<WString> resolvers;
			for (vint i = 0; i <= maxPass; i++)
			{
				context.passIndex = i;
				{
					manager->GetPerResourceResolverNames(i, resolvers);
					if (resolvers.Count() > 0)
					{
						PrecompileResourceFolder(context, callback, errors);
					}
				}
				{
					manager->GetPerPassResolverNames(i, resolvers);
					if (resolvers.Count() > 0)
					{
						if (callback)
						{
							callback->OnPerPass(i);
						}
						FOREACH(WString, name, resolvers)
						{
							auto resolver = manager->GetTypeResolver(name);
							resolver->Precompile()->PerPassPrecompile(context, errors);
						}
					}
				}
				if (errors.Count() > 0)
				{
					return context.targetFolder;
				}
			}
			AddFolder(L"Precompiled", context.targetFolder);
			return context.targetFolder;
		}

		void GuiResource::Initialize(GuiResourceUsage usage)
		{
			auto precompiledFolder = GetFolder(L"Precompiled");
			if (!precompiledFolder)
			{
				CHECK_FAIL(L"GuiResource::Initialize()#Cannot initialize a non-precompiled resource.");
				return;
			}
			
			GuiResourceInitializeContext context;
			context.rootResource = this;
			context.resolver = new GuiResourcePathResolver(this, workingDirectory);
			context.targetFolder = precompiledFolder;
			context.usage = usage;

			vint maxPass = GetResourceResolverManager()->GetMaxInitializePassIndex();
			for (vint i = 0; i <= maxPass; i++)
			{
				context.passIndex = i;
				InitializeResourceFolder(context);
			}
		}

		Ptr<DocumentModel> GuiResource::GetDocumentByPath(const WString& path)
		{
			Ptr<DocumentModel> result=GetValueByPath(path).Cast<DocumentModel>();
			if(!result) throw ArgumentException(L"Path not exists.", L"GuiResource::GetDocumentByPath", L"path");
			return result;
		}

		Ptr<GuiImageData> GuiResource::GetImageByPath(const WString& path)
		{
			Ptr<GuiImageData> result=GetValueByPath(path).Cast<GuiImageData>();
			if(!result) throw ArgumentException(L"Path not exists.", L"GuiResource::GetImageByPath", L"path");
			return result;
		}

		Ptr<parsing::xml::XmlDocument> GuiResource::GetXmlByPath(const WString& path)
		{
			Ptr<XmlDocument> result=GetValueByPath(path).Cast<XmlDocument>();
			if(!result) throw ArgumentException(L"Path not exists.", L"GuiResource::GetXmlByPath", L"path");
			return result;
		}

		WString GuiResource::GetStringByPath(const WString& path)
		{
			Ptr<ObjectBox<WString>> result=GetValueByPath(path).Cast<ObjectBox<WString>>();
			if(!result) throw ArgumentException(L"Path not exists.", L"GuiResource::GetStringByPath", L"path");
			return result->Unbox();
		}

/***********************************************************************
GuiResourcePathResolver
***********************************************************************/

		GuiResourcePathResolver::GuiResourcePathResolver(Ptr<GuiResource> _resource, const WString& _workingDirectory)
			:resource(_resource)
			,workingDirectory(_workingDirectory)
		{
		}

		GuiResourcePathResolver::~GuiResourcePathResolver()
		{
		}

		Ptr<DescriptableObject> GuiResourcePathResolver::ResolveResource(const WString& protocol, const WString& path)
		{
			Ptr<IGuiResourcePathResolver> resolver;
			vint index=resolvers.Keys().IndexOf(protocol);
			if(index==-1)
			{
				IGuiResourcePathResolverFactory* factory=GetResourceResolverManager()->GetPathResolverFactory(protocol);
				if(factory)
				{
					resolver=factory->CreateResolver(resource, workingDirectory);
				}
				resolvers.Add(protocol, resolver);
			}
			else
			{
				resolver=resolvers.Values()[index];
			}

			if(resolver)
			{
				return resolver->ResolveResource(path);
			}
			else
			{
				return 0;
			}
		}

/***********************************************************************
GuiResourcePathFileResolver
***********************************************************************/

		class GuiResourcePathFileResolver : public Object, public IGuiResourcePathResolver
		{
		protected:
			WString					workingDirectory;

		public:
			GuiResourcePathFileResolver(const WString& _workingDirectory)
				:workingDirectory(_workingDirectory)
			{
			}

			Ptr<DescriptableObject> ResolveResource(const WString& path)
			{
				WString filename=path;
				if(filename.Length()>=2 && filename[1]!=L':')
				{
					filename=workingDirectory+filename;
				}
				Ptr<INativeImage> image=GetCurrentController()->ImageService()->CreateImageFromFile(filename);
				return new GuiImageData(image, 0);
			}

			class Factory : public Object, public IGuiResourcePathResolverFactory
			{
			public:
				WString GetProtocol()override
				{
					return L"file";
				}

				Ptr<IGuiResourcePathResolver> CreateResolver(Ptr<GuiResource> resource, const WString& workingDirectory)override
				{
					return new GuiResourcePathFileResolver(workingDirectory);
				}
			};
		};

/***********************************************************************
GuiResourcePathResResolver
***********************************************************************/

		class GuiResourcePathResResolver : public Object, public IGuiResourcePathResolver
		{
		protected:
			Ptr<GuiResource>		resource;

		public:
			GuiResourcePathResResolver(Ptr<GuiResource> _resource)
				:resource(_resource)
			{
			}

			Ptr<DescriptableObject> ResolveResource(const WString& path)
			{
				if(resource)
				{
					if(path.Length()>0)
					{
						switch(path[path.Length()-1])
						{
						case L'\\':case L'/':
							return resource->GetFolderByPath(path);
						default:
							return resource->GetValueByPath(path);
						}
					}
				}
				return 0;
			}

			class Factory : public Object, public IGuiResourcePathResolverFactory
			{
			public:
				WString GetProtocol()override
				{
					return L"res";
				}

				Ptr<IGuiResourcePathResolver> CreateResolver(Ptr<GuiResource> resource, const WString& workingDirectory)override
				{
					return new GuiResourcePathResResolver(resource);
				}
			};
		};

/***********************************************************************
IGuiResourceResolverManager
***********************************************************************/

		IGuiResourceResolverManager* resourceResolverManager=0;

		IGuiResourceResolverManager* GetResourceResolverManager()
		{
			return resourceResolverManager;
		}

		class GuiResourceResolverManager : public Object, public IGuiResourceResolverManager, public IGuiPlugin
		{
			typedef Dictionary<WString, Ptr<IGuiResourcePathResolverFactory>>			PathFactoryMap;
			typedef Dictionary<WString, Ptr<IGuiResourceTypeResolver>>					TypeResolverMap;
			typedef Group<vint, WString>												ResolverGroup;
		protected:
			PathFactoryMap				pathFactories;
			TypeResolverMap				typeResolvers;
			ResolverGroup				perResourceResolvers;
			ResolverGroup				perPassResolvers;

		public:
			
			GUI_PLUGIN_NAME(GacUI_Res_ResourceResolver)
			{
			}

			void Load()override
			{
				globalStringKeyManager = new GlobalStringKeyManager();
				globalStringKeyManager->InitializeConstants();

				resourceResolverManager = this;
				SetPathResolverFactory(new GuiResourcePathFileResolver::Factory);
				SetPathResolverFactory(new GuiResourcePathResResolver::Factory);
			}

			void Unload()override
			{
				delete globalStringKeyManager;
				globalStringKeyManager = 0;
				resourceResolverManager = 0;
			}

			IGuiResourcePathResolverFactory* GetPathResolverFactory(const WString& protocol)override
			{
				vint index=pathFactories.Keys().IndexOf(protocol);
				return index==-1?0:pathFactories.Values()[index].Obj();
			}

			bool SetPathResolverFactory(Ptr<IGuiResourcePathResolverFactory> factory)override
			{
				if(pathFactories.Keys().Contains(factory->GetProtocol())) return false;
				pathFactories.Add(factory->GetProtocol(), factory);
				return true;
			}

			IGuiResourceTypeResolver* GetTypeResolver(const WString& type)override
			{
				vint index=typeResolvers.Keys().IndexOf(type);
				return index==-1?0:typeResolvers.Values()[index].Obj();
			}

			bool SetTypeResolver(Ptr<IGuiResourceTypeResolver> resolver)override
			{
				if(typeResolvers.Keys().Contains(resolver->GetType())) return false;
				typeResolvers.Add(resolver->GetType(), resolver);

				if (auto precompile = resolver->Precompile())
				{
					vint maxPassIndex = precompile->GetMaxPassIndex();
					for (vint i = 0; i <= maxPassIndex; i++)
					{
						switch (precompile->GetPassSupport(i))
						{
						case IGuiResourceTypeResolver_Precompile::PerResource:
							perResourceResolvers.Add(i, resolver->GetType());
							break;
						case IGuiResourceTypeResolver_Precompile::PerPass:
							perPassResolvers.Add(i, resolver->GetType());
							break;
						default:;
						}
					}
				}

				return true;
			}

			vint GetMaxPrecompilePassIndex()override
			{
				vint maxPass = -1;
				FOREACH(Ptr<IGuiResourceTypeResolver>, resolver, typeResolvers.Values())
				{
					if (auto precompile = resolver->Precompile())
					{
						vint pass = precompile->GetMaxPassIndex();
						if (maxPass < pass)
						{
							maxPass = pass;
						}
					}
				}
				return maxPass;
			}

			vint GetMaxInitializePassIndex()override
			{
				vint maxPass = -1;
				FOREACH(Ptr<IGuiResourceTypeResolver>, resolver, typeResolvers.Values())
				{
					if (auto initialize = resolver->Initialize())
					{
						vint pass = initialize->GetMaxPassIndex();
						if (maxPass < pass)
						{
							maxPass = pass;
						}
					}
				}
				return maxPass;
			}

			void GetPerResourceResolverNames(vint passIndex, collections::List<WString>& names)override
			{
				names.Clear();
				vint index = perResourceResolvers.Keys().IndexOf(passIndex);
				if (index != -1)
				{
					CopyFrom(names, perResourceResolvers.GetByIndex(index));
				}
			}

			void GetPerPassResolverNames(vint passIndex, collections::List<WString>& names)override
			{
				names.Clear();
				vint index = perPassResolvers.Keys().IndexOf(passIndex);
				if (index != -1)
				{
					CopyFrom(names, perPassResolvers.GetByIndex(index));
				}
			}
		};
		GUI_REGISTER_PLUGIN(GuiResourceResolverManager)

/***********************************************************************
Helpers
***********************************************************************/

		vint CopyStream(stream::IStream& inputStream, stream::IStream& outputStream)
		{
			vint totalSize = 0;
			while (true)
			{
				char buffer[1024];
				vint copied = inputStream.Read(buffer, (vint)sizeof(buffer));
				if (copied == 0)
				{
					break;
				}
				totalSize += outputStream.Write(buffer, copied);
			}
			return totalSize;
		}

		const vint CompressionFragmentSize = 1048576;

		void CompressStream(stream::IStream& inputStream, stream::IStream& outputStream)
		{
			Array<char> buffer(CompressionFragmentSize);
			while (true)
			{
				vint size = inputStream.Read(&buffer[0], buffer.Count());
				if (size == 0) break;

				MemoryStream compressedStream;
				{
					LzwEncoder encoder;
					EncoderStream encoderStream(compressedStream, encoder);
					encoderStream.Write(&buffer[0], size);
				}

				compressedStream.SeekFromBegin(0);
				{
					{
						vint32_t bufferSize = (vint32_t)size;
						outputStream.Write(&bufferSize, (vint)sizeof(bufferSize));
					}
					{
						vint32_t compressedSize = (vint32_t)compressedStream.Size();
						outputStream.Write(&compressedSize, (vint)sizeof(compressedSize));
					}
					CopyStream(compressedStream, outputStream);
				}
			}
		}

		void DecompressStream(stream::IStream& inputStream, stream::IStream& outputStream)
		{
			vint totalSize = 0;
			vint totalWritten = 0;
			while (true)
			{
				vint32_t bufferSize = 0;
				if (inputStream.Read(&bufferSize, (vint)sizeof(bufferSize)) != sizeof(bufferSize))
				{
					break;
				}

				vint32_t compressedSize = 0;
				CHECK_ERROR(inputStream.Read(&compressedSize, (vint)sizeof(compressedSize)) == sizeof(compressedSize), L"vl::presentation::DecompressStream(MemoryStream&, MemoryStream&)#Incomplete input");

				Array<char> buffer(compressedSize);
				CHECK_ERROR(inputStream.Read(&buffer[0], compressedSize) == compressedSize, L"vl::presentation::DecompressStream(MemoryStream&, MemoryStream&)#Incomplete input");

				MemoryWrapperStream compressedStream(&buffer[0], compressedSize);
				LzwDecoder decoder;
				DecoderStream decoderStream(compressedStream, decoder);
				totalWritten += CopyStream(decoderStream, outputStream);
				totalSize += bufferSize;
			}
			CHECK_ERROR(outputStream.Size() == totalSize, L"vl::presentation::DecompressStream(MemoryStream&, MemoryStream&)#Incomplete input");
		}

		void DecompressStream(const char** buffer, bool decompress, vint rows, vint block, vint remain, stream::IStream& outputStream)
		{
			if (decompress)
			{
				MemoryStream compressedStream;
				for (vint i = 0; i < rows; i++)
				{
					vint size = i == rows - 1 ? remain : block;
					compressedStream.Write((void*)buffer[i], size);
				}
				compressedStream.SeekFromBegin(0);
				DecompressStream(compressedStream, outputStream);
			}
			else
			{
				for (vint i = 0; i < rows; i++)
				{
					vint size = i == rows - 1 ? remain : block;
					outputStream.Write((void*)buffer[i], size);
				}
			}
		}
	}
}


/***********************************************************************
.\RESOURCES\GUIRESOURCETYPERESOLVERS.CPP
***********************************************************************/

namespace vl
{
	namespace presentation
	{
		using namespace collections;
		using namespace controls;
		using namespace parsing;
		using namespace parsing::tabling;
		using namespace parsing::xml;
		using namespace stream;

/***********************************************************************
Image Type Resolver (Image)
***********************************************************************/

		class GuiResourceImageTypeResolver
			: public Object
			, public IGuiResourceTypeResolver
			, private IGuiResourceTypeResolver_DirectLoadXml
			, private IGuiResourceTypeResolver_DirectLoadStream
		{
		public:
			WString GetType()override
			{
				return L"Image";
			}

			bool XmlSerializable()override
			{
				return true;
			}

			bool StreamSerializable()override
			{
				return true;
			}

			IGuiResourceTypeResolver_DirectLoadXml* DirectLoadXml()override
			{
				return this;
			}

			IGuiResourceTypeResolver_DirectLoadStream* DirectLoadStream()override
			{
				return this;
			}

			Ptr<parsing::xml::XmlElement> Serialize(Ptr<GuiResourceItem> resource, Ptr<DescriptableObject> content)override
			{
				return nullptr;
			}

			void SerializePrecompiled(Ptr<GuiResourceItem> resource, Ptr<DescriptableObject> content, stream::IStream& stream)override
			{
				auto obj = content.Cast<GuiImageData>();
				stream::internal::ContextFreeWriter writer(stream);
				FileStream fileStream(resource->GetFileAbsolutePath(), FileStream::ReadOnly);
				writer << (stream::IStream&)fileStream;
			}

			Ptr<DescriptableObject> ResolveResource(Ptr<GuiResourceItem> resource, Ptr<parsing::xml::XmlElement> element, GuiResourceError::List& errors)override
			{
				errors.Add(GuiResourceError({ resource }, L"Image should load from file."));
				return nullptr;
			}

			Ptr<DescriptableObject> ResolveResource(Ptr<GuiResourceItem> resource, const WString& path, GuiResourceError::List& errors)override
			{
				Ptr<INativeImage> image = GetCurrentController()->ImageService()->CreateImageFromFile(path);
				if(image)
				{
					return new GuiImageData(image, 0);
				}
				else
				{
					errors.Add(GuiResourceError({ resource }, L"Failed to load file \"" + path + L"\"."));
					return nullptr;
				}
			}

			Ptr<DescriptableObject> ResolveResourcePrecompiled(Ptr<GuiResourceItem> resource, stream::IStream& stream, GuiResourceError::List& errors)override
			{
				stream::internal::ContextFreeReader reader(stream);
				MemoryStream memoryStream;
				reader << (stream::IStream&)memoryStream;

				auto image = GetCurrentController()->ImageService()->CreateImageFromStream(memoryStream);
				if (image)
				{
					return new GuiImageData(image, 0);
				}
				else
				{
					errors.Add(GuiResourceError({ resource }, L"[BINARY] Failed to load an image from binary data in a stream."));
					return nullptr;
				}
			}
		};

/***********************************************************************
Text Type Resolver (Text)
***********************************************************************/

		class GuiResourceTextTypeResolver
			: public Object
			, public IGuiResourceTypeResolver
			, private IGuiResourceTypeResolver_DirectLoadXml
			, private IGuiResourceTypeResolver_DirectLoadStream
		{
		public:
			WString GetType()override
			{
				return L"Text";
			}

			bool XmlSerializable()override
			{
				return true;
			}

			bool StreamSerializable()override
			{
				return true;
			}

			IGuiResourceTypeResolver_DirectLoadXml* DirectLoadXml()override
			{
				return this;
			}

			IGuiResourceTypeResolver_DirectLoadStream* DirectLoadStream()override
			{
				return this;
			}

			Ptr<parsing::xml::XmlElement> Serialize(Ptr<GuiResourceItem> resource, Ptr<DescriptableObject> content)override
			{
				if (auto obj = content.Cast<GuiTextData>())
				{
					auto xmlContent = MakePtr<XmlText>();
					xmlContent->content.value = obj->GetText();

					auto xmlText = MakePtr<XmlElement>();
					xmlText->name.value = L"Text";
					xmlText->subNodes.Add(xmlContent);

					return xmlText;
				}
				return 0;
			}

			void SerializePrecompiled(Ptr<GuiResourceItem> resource, Ptr<DescriptableObject> content, stream::IStream& stream)override
			{
				auto obj = content.Cast<GuiTextData>();
				stream::internal::ContextFreeWriter writer(stream);
				WString text = obj->GetText();
				writer << text;
			}

			Ptr<DescriptableObject> ResolveResource(Ptr<GuiResourceItem> resource, Ptr<parsing::xml::XmlElement> element, GuiResourceError::List& errors)override
			{
				return new GuiTextData(XmlGetValue(element));
			}

			Ptr<DescriptableObject> ResolveResource(Ptr<GuiResourceItem> resource, const WString& path, GuiResourceError::List& errors)override
			{
				WString text;
				if(LoadTextFile(path, text))
				{
					return new GuiTextData(text);
				}
				else
				{
					errors.Add(GuiResourceError({ resource }, L"Failed to load file \"" + path + L"\"."));
					return 0;
				}
			}

			Ptr<DescriptableObject> ResolveResourcePrecompiled(Ptr<GuiResourceItem> resource, stream::IStream& stream, GuiResourceError::List& errors)override
			{
				stream::internal::ContextFreeReader reader(stream);
				WString text;
				reader << text;
				return new GuiTextData(text);
			}
		};

/***********************************************************************
Xml Type Resolver (Xml)
***********************************************************************/

		class GuiResourceXmlTypeResolver
			: public Object
			, public IGuiResourceTypeResolver
			, private IGuiResourceTypeResolver_DirectLoadXml
			, private IGuiResourceTypeResolver_DirectLoadStream
		{
		public:
			WString GetType()override
			{
				return L"Xml";
			}

			bool XmlSerializable()override
			{
				return true;
			}

			bool StreamSerializable()override
			{
				return true;
			}

			IGuiResourceTypeResolver_DirectLoadXml* DirectLoadXml()override
			{
				return this;
			}

			IGuiResourceTypeResolver_DirectLoadStream* DirectLoadStream()override
			{
				return this;
			}

			Ptr<parsing::xml::XmlElement> Serialize(Ptr<GuiResourceItem> resource, Ptr<DescriptableObject> content)override
			{
				if (auto obj = content.Cast<XmlDocument>())
				{
					auto xmlXml = MakePtr<XmlElement>();
					xmlXml->name.value = L"Xml";
					xmlXml->subNodes.Add(obj->rootElement);
					return xmlXml;
				}
				return nullptr;
			}

			void SerializePrecompiled(Ptr<GuiResourceItem> resource, Ptr<DescriptableObject> content, stream::IStream& stream)override
			{
				auto obj = content.Cast<XmlDocument>();
				MemoryStream buffer;
				{
					StreamWriter writer(buffer);
					XmlPrint(obj, writer);
				}
				{
					buffer.SeekFromBegin(0);
					StreamReader reader(buffer);
					WString text = reader.ReadToEnd();

					stream::internal::ContextFreeWriter writer(stream);
					writer << text;
				}
			}

			Ptr<DescriptableObject> ResolveResource(Ptr<GuiResourceItem> resource, Ptr<parsing::xml::XmlElement> element, GuiResourceError::List& errors)override
			{
				Ptr<XmlElement> root = XmlGetElements(element).First(0);
				if(root)
				{
					Ptr<XmlDocument> xml=new XmlDocument;
					xml->rootElement=root;
					return xml;
				}
				return nullptr;
			}

			Ptr<DescriptableObject> ResolveResource(Ptr<GuiResourceItem> resource, const WString& path, GuiResourceError::List& errors)override
			{
				if (auto parser = GetParserManager()->GetParser<XmlDocument>(L"XML"))
				{
					WString text;
					if (LoadTextFile(path, text))
					{
						return parser->Parse({ resource }, text, errors);
					}
					else
					{
						errors.Add(GuiResourceError({ resource }, L"Failed to load file \"" + path + L"\"."));
					}
				}
				return nullptr;
			}

			Ptr<DescriptableObject> ResolveResourcePrecompiled(Ptr<GuiResourceItem> resource, stream::IStream& stream, GuiResourceError::List& errors)override
			{
				if (auto parser = GetParserManager()->GetParser<XmlDocument>(L"XML"))
				{
					stream::internal::ContextFreeReader reader(stream);
					WString text;
					reader << text;

					return parser->Parse({ resource }, text, errors);
				}
				return nullptr;
			}
		};

/***********************************************************************
Doc Type Resolver (Doc)
***********************************************************************/

		class GuiResourceDocTypeResolver
			: public Object
			, public IGuiResourceTypeResolver
			, private IGuiResourceTypeResolver_IndirectLoad
		{
		public:
			WString GetType()override
			{
				return L"Doc";
			}

			bool XmlSerializable()override
			{
				return true;
			}

			bool StreamSerializable()override
			{
				return true;
			}

			WString GetPreloadType()override
			{
				return L"Xml";
			}

			bool IsDelayLoad()override
			{
				return true;
			}

			IGuiResourceTypeResolver_IndirectLoad* IndirectLoad()override
			{
				return this;
			}

			Ptr<DescriptableObject> Serialize(Ptr<GuiResourceItem> resource, Ptr<DescriptableObject> content)override
			{
				if (auto obj = content.Cast<DocumentModel>())
				{
					return obj->SaveToXml();
				}
				return nullptr;
			}

			Ptr<DescriptableObject> ResolveResource(Ptr<GuiResourceItem> resource, Ptr<GuiResourcePathResolver> resolver, GuiResourceError::List& errors)override
			{
				if(auto xml = resource->GetContent().Cast<XmlDocument>())
				{
					Ptr<DocumentModel> model = DocumentModel::LoadFromXml(resource, xml, resolver, errors);
					return model;
				}
				return nullptr;
			}
		};

/***********************************************************************
Type Resolver Plugin
***********************************************************************/

		class GuiResourceTypeResolversPlugin : public Object, public IGuiPlugin
		{
		public:

			GUI_PLUGIN_NAME(GacUI_Res_TypeResolvers)
			{
				GUI_PLUGIN_DEPEND(GacUI_Res_ResourceResolver);
			}

			void Load()override
			{
				IGuiResourceResolverManager* manager=GetResourceResolverManager();
				manager->SetTypeResolver(new GuiResourceImageTypeResolver);
				manager->SetTypeResolver(new GuiResourceTextTypeResolver);
				manager->SetTypeResolver(new GuiResourceXmlTypeResolver);
				manager->SetTypeResolver(new GuiResourceDocTypeResolver);
			}

			void Unload()override
			{
			}
		};
		GUI_REGISTER_PLUGIN(GuiResourceTypeResolversPlugin)
	}
}


/***********************************************************************
.\GACUIREFLECTIONHELPER.CPP
***********************************************************************/

namespace vl
{
	namespace reflection
	{
		namespace description
		{
			using namespace parsing;
			using namespace parsing::tabling;
			using namespace parsing::xml;
			using namespace stream;
			using namespace collections;
			using namespace presentation;
			using namespace presentation::elements;
			using namespace presentation::compositions;
			using namespace presentation::controls;
			using namespace presentation::theme;
			using namespace presentation::templates;

/***********************************************************************
Serialization (Color)
***********************************************************************/

			Color TypedValueSerializerProvider<Color>::GetDefaultValue()
			{
				return Color();
			}

			bool TypedValueSerializerProvider<Color>::Serialize(const Color& input, WString& output)
			{
				output = input.ToString();
				return true;
			}

			bool TypedValueSerializerProvider<Color>::Deserialize(const WString& input, Color& output)
			{
				output = Color::Parse(input);
				return true;
			}

			IBoxedValue::CompareResult TypedValueSerializerProvider<Color>::Compare(const presentation::Color& a, const presentation::Color& b)
			{
				return TypedValueSerializerProvider<vuint32_t>::Compare(a.value, b.value);
			}

/***********************************************************************
Serialization (DocumentFontSize)
***********************************************************************/

			DocumentFontSize TypedValueSerializerProvider<DocumentFontSize>::GetDefaultValue()
			{
				return DocumentFontSize();
			}

			bool TypedValueSerializerProvider<DocumentFontSize>::Serialize(const DocumentFontSize& input, WString& output)
			{
				output = input.ToString();
				return true;
			}

			bool TypedValueSerializerProvider<DocumentFontSize>::Deserialize(const WString& input, DocumentFontSize& output)
			{
				output = DocumentFontSize::Parse(input);
				return true;
			}

			IBoxedValue::CompareResult TypedValueSerializerProvider<DocumentFontSize>::Compare(const presentation::DocumentFontSize& a, const presentation::DocumentFontSize& b)
			{
				return TypedValueSerializerProvider<WString>::Compare(a.ToString(), b.ToString());
			}

/***********************************************************************
Serialization (GlobalStringKey)
***********************************************************************/

			GlobalStringKey TypedValueSerializerProvider<GlobalStringKey>::GetDefaultValue()
			{
				return GlobalStringKey();
			}

			bool TypedValueSerializerProvider<GlobalStringKey>::Serialize(const GlobalStringKey& input, WString& output)
			{
				output = input.ToString();
				return true;
			}

			bool TypedValueSerializerProvider<GlobalStringKey>::Deserialize(const WString& input, GlobalStringKey& output)
			{
				output = GlobalStringKey::Get(input);
				return true;
			}

			IBoxedValue::CompareResult TypedValueSerializerProvider<GlobalStringKey>::Compare(const presentation::GlobalStringKey& a, const presentation::GlobalStringKey& b)
			{
				return TypedValueSerializerProvider<WString>::Compare(a.ToString(), b.ToString());
			}

/***********************************************************************
External Functions (Basic)
***********************************************************************/

			Ptr<INativeImage> INativeImage_Constructor(const WString& path)
			{
				return GetCurrentController()->ImageService()->CreateImageFromFile(path);
			}

			INativeCursor* INativeCursor_Constructor1()
			{
				return GetCurrentController()->ResourceService()->GetDefaultSystemCursor();
			}

			INativeCursor* INativeCursor_Constructor2(INativeCursor::SystemCursorType type)
			{
				return GetCurrentController()->ResourceService()->GetSystemCursor(type);
			}

/***********************************************************************
External Functions (Elements)
***********************************************************************/

			text::TextLines* GuiColorizedTextElement_GetLines(GuiColorizedTextElement* thisObject)
			{
				return &thisObject->GetLines();
			}

/***********************************************************************
External Functions (Compositions)
***********************************************************************/

			void GuiTableComposition_SetRows(GuiTableComposition* thisObject, vint value)
			{
				vint columns = thisObject->GetColumns();
				if (columns <= 0) columns = 1;
				thisObject->SetRowsAndColumns(value, columns);
			}

			void GuiTableComposition_SetColumns(GuiTableComposition* thisObject, vint value)
			{
				vint row = thisObject->GetRows();
				if (row <= 0) row = 1;
				thisObject->SetRowsAndColumns(row, value);
			}

			void IGuiAltActionHost_CollectAltActions(IGuiAltActionHost* host, List<IGuiAltAction*>& actions)
			{
				Group<WString, IGuiAltAction*> group;
				host->CollectAltActions(group);
				for (vint i = 0; i < group.Count(); i++)
				{
					CopyFrom(actions, group.GetByIndex(i), true);
				}
			}
		}
	}
}

/***********************************************************************
.\RESOURCES\GUIRESOURCEMANAGER.CPP
***********************************************************************/

namespace vl
{
	namespace presentation
	{
		using namespace collections;
		using namespace stream;
		using namespace reflection::description;
		using namespace controls;

/***********************************************************************
Class Name Record (ClassNameRecord)
***********************************************************************/

		class GuiResourceClassNameRecordTypeResolver
			: public Object
			, public IGuiResourceTypeResolver
			, private IGuiResourceTypeResolver_DirectLoadStream
		{
		public:
			WString GetType()override
			{
				return L"ClassNameRecord";
			}

			bool XmlSerializable()override
			{
				return false;
			}

			bool StreamSerializable()override
			{
				return true;
			}

			IGuiResourceTypeResolver_DirectLoadStream* DirectLoadStream()override
			{
				return this;
			}

			void SerializePrecompiled(Ptr<GuiResourceItem> resource, Ptr<DescriptableObject> content, stream::IStream& stream)override
			{
				if (auto obj = content.Cast<GuiResourceClassNameRecord>())
				{
					internal::ContextFreeWriter writer(stream);
					writer << obj->classNames;
				}
			}

			Ptr<DescriptableObject> ResolveResourcePrecompiled(Ptr<GuiResourceItem> resource, stream::IStream& stream, GuiResourceError::List& errors)override
			{
				internal::ContextFreeReader reader(stream);

				auto obj = MakePtr<GuiResourceClassNameRecord>();
				reader << obj->classNames;
				return obj;
			}
		};

/***********************************************************************
IGuiInstanceResourceManager
***********************************************************************/

		IGuiResourceManager* resourceManager = nullptr;

		IGuiResourceManager* GetResourceManager()
		{
			return resourceManager;
		}

		class GuiResourceManager : public Object, public IGuiResourceManager, public IGuiPlugin
		{
		protected:
			typedef Dictionary<WString, Ptr<GuiResource>>					ResourceMap;

			ResourceMap								resources;
			ResourceMap								instanceResources;

		public:

			GUI_PLUGIN_NAME(GacUI_Res_Resource)
			{
				GUI_PLUGIN_DEPEND(GacUI_Res_ResourceResolver);
			}

			void Load()override
			{
				resourceManager = this;
				IGuiResourceResolverManager* manager = GetResourceResolverManager();
				manager->SetTypeResolver(new GuiResourceClassNameRecordTypeResolver);
			}

			void Unload()override
			{
				resourceManager = nullptr;
			}

			bool SetResource(const WString& name, Ptr<GuiResource> resource, GuiResourceUsage usage)override
			{
				vint index = resources.Keys().IndexOf(name);
				if (index != -1) return false;
				
				resource->Initialize(usage);
				resources.Add(name, resource);
				
				auto record = resource->GetValueByPath(L"Precompiled/ClassNameRecord").Cast<GuiResourceClassNameRecord>();
				FOREACH(WString, className, record->classNames)
				{
					instanceResources.Add(className, resource);
				}
				return true;
			}

			Ptr<GuiResource> GetResource(const WString& name)override
			{
				vint index = resources.Keys().IndexOf(name);
				return index == -1 ? nullptr : resources.Values()[index];
			}

			Ptr<GuiResource> GetResourceFromClassName(const WString& classFullName)override
			{
				vint index = instanceResources.Keys().IndexOf(classFullName);
				if (index == -1) return nullptr;
				return instanceResources.Values()[index];
			}
		};
		GUI_REGISTER_PLUGIN(GuiResourceManager)
	}
}


/***********************************************************************
.\RESOURCES\GUIDOCUMENTEDITOR_ADDCONTAINER.CPP
***********************************************************************/

namespace vl
{
	namespace presentation
	{
		using namespace collections;
		using namespace document_editor;

/***********************************************************************
Insert container runs on top of all text ranges that intersect with the specified range
AddStyleVisitor		: Apply a style on the specified range
AddHyperlinkVisitor	: Apply a hyperlink on the specified range
AddStyleNameVisitor	: Apply a style name on the specified range
***********************************************************************/

		namespace document_operation_visitors
		{
			class AddContainerVisitor : public Object, public DocumentRun::IVisitor
			{
			public:
				RunRangeMap&							runRanges;
				vint									start;
				vint									end;
				bool									insertStyle;

				virtual Ptr<DocumentContainerRun>		CreateContainer() = 0;

				AddContainerVisitor(RunRangeMap& _runRanges, vint _start, vint _end)
					:runRanges(_runRanges)
					, start(_start)
					, end(_end)
					, insertStyle(false)
				{
				}

				void VisitContainer(DocumentContainerRun* run)
				{
					for (vint i = run->runs.Count() - 1; i >= 0; i--)
					{
						Ptr<DocumentRun> subRun = run->runs[i];
						RunRange range = runRanges[subRun.Obj()];
						if (range.start<end && start<range.end)
						{
							insertStyle = false;
							subRun->Accept(this);
							if (insertStyle)
							{
								Ptr<DocumentContainerRun> containerRun = CreateContainer();
								run->runs.RemoveAt(i);
								containerRun->runs.Add(subRun);
								run->runs.Insert(i, containerRun);
							}
						}
					}
					insertStyle = false;
				}

				void Visit(DocumentTextRun* run)override
				{
					insertStyle = true;
				}

				void Visit(DocumentStylePropertiesRun* run)override
				{
					VisitContainer(run);
				}

				void Visit(DocumentStyleApplicationRun* run)override
				{
					VisitContainer(run);
				}

				void Visit(DocumentHyperlinkRun* run)override
				{
					VisitContainer(run);
				}

				void Visit(DocumentImageRun* run)override
				{
					insertStyle = false;
				}

				void Visit(DocumentEmbeddedObjectRun* run)override
				{
					insertStyle = false;
				}

				void Visit(DocumentParagraphRun* run)override
				{
					VisitContainer(run);
				}
			};

			class AddStyleVisitor : public AddContainerVisitor
			{
			public:
				Ptr<DocumentStyleProperties>	style;

				Ptr<DocumentContainerRun> CreateContainer()override
				{
					Ptr<DocumentStylePropertiesRun> containerRun = new DocumentStylePropertiesRun;
					containerRun->style = CopyStyle(style);
					return containerRun;
				}

				AddStyleVisitor(RunRangeMap& _runRanges, vint _start, vint _end, Ptr<DocumentStyleProperties> _style)
					:AddContainerVisitor(_runRanges, _start, _end)
					, style(_style)
				{
				}
			};

			class AddHyperlinkVisitor : public AddContainerVisitor
			{
			public:
				WString							reference;
				WString							normalStyleName;
				WString							activeStyleName;

				Ptr<DocumentContainerRun> CreateContainer()override
				{
					Ptr<DocumentHyperlinkRun> containerRun = new DocumentHyperlinkRun;
					containerRun->reference = reference;
					containerRun->normalStyleName = normalStyleName;
					containerRun->activeStyleName = activeStyleName;
					containerRun->styleName = normalStyleName;
					return containerRun;
				}

				AddHyperlinkVisitor(RunRangeMap& _runRanges, vint _start, vint _end, const WString& _reference, const WString& _normalStyleName, const WString& _activeStyleName)
					:AddContainerVisitor(_runRanges, _start, _end)
					, reference(_reference)
					, normalStyleName(_normalStyleName)
					, activeStyleName(_activeStyleName)
				{
				}
			};

			class AddStyleNameVisitor : public AddContainerVisitor
			{
			public:
				WString							styleName;

				Ptr<DocumentContainerRun> CreateContainer()override
				{
					Ptr<DocumentStyleApplicationRun> containerRun = new DocumentStyleApplicationRun;
					containerRun->styleName = styleName;
					return containerRun;
				}

				AddStyleNameVisitor(RunRangeMap& _runRanges, vint _start, vint _end, const WString& _styleName)
					:AddContainerVisitor(_runRanges, _start, _end)
					, styleName(_styleName)
				{
				}
			};
		}
		using namespace document_operation_visitors;

		namespace document_editor
		{
			void AddStyle(DocumentParagraphRun* run, RunRangeMap& runRanges, vint start, vint end, Ptr<DocumentStyleProperties> style)
			{
				AddStyleVisitor visitor(runRanges, start, end, style);
				run->Accept(&visitor);
			}

			void AddHyperlink(DocumentParagraphRun* run, RunRangeMap& runRanges, vint start, vint end, const WString& reference, const WString& normalStyleName, const WString& activeStyleName)
			{
				AddHyperlinkVisitor visitor(runRanges, start, end, reference, normalStyleName, activeStyleName);
				run->Accept(&visitor);
			}

			void AddStyleName(DocumentParagraphRun* run, RunRangeMap& runRanges, vint start, vint end, const WString& styleName)
			{
				AddStyleNameVisitor visitor(runRanges, start, end, styleName);
				run->Accept(&visitor);
			}
		}
	}
}

/***********************************************************************
.\RESOURCES\GUIDOCUMENTEDITOR_CLEARUNNECESSARYRUN.CPP
***********************************************************************/

namespace vl
{
	namespace presentation
	{
		using namespace collections;
		using namespace document_editor;

/***********************************************************************
Clear all runs that have an empty length
***********************************************************************/

		namespace document_operation_visitors
		{
			class ClearRunVisitor : public Object, public DocumentRun::IVisitor
			{
			public:
				vint							start;

				ClearRunVisitor()
					:start(0)
				{
				}

				void VisitContainer(DocumentContainerRun* run)
				{
					for (vint i = run->runs.Count() - 1; i >= 0; i--)
					{
						vint oldStart = start;
						run->runs[i]->Accept(this);
						if (oldStart == start)
						{
							run->runs.RemoveAt(i);
						}
					}
				}

				void VisitContent(DocumentContentRun* run)
				{
					start += run->GetRepresentationText().Length();
				}

				void Visit(DocumentTextRun* run)override
				{
					VisitContent(run);
				}

				void Visit(DocumentStylePropertiesRun* run)override
				{
					VisitContainer(run);
				}

				void Visit(DocumentStyleApplicationRun* run)override
				{
					VisitContainer(run);
				}

				void Visit(DocumentHyperlinkRun* run)override
				{
					VisitContainer(run);
				}

				void Visit(DocumentImageRun* run)override
				{
					VisitContent(run);
				}

				void Visit(DocumentEmbeddedObjectRun* run)override
				{
					VisitContent(run);
				}

				void Visit(DocumentParagraphRun* run)override
				{
					VisitContainer(run);
				}
			};
		}
		using namespace document_operation_visitors;

/***********************************************************************
Ensure DocumentStylePropertiesRun doesn't have a child which is another DocumentStylePropertiesRun
Remove DocumentStylePropertiesRun's property if it set a value but doesn't change the output
Remove DocumentStylePropertiesRun if it is empty or contains no text run
***********************************************************************/

		namespace document_operation_visitors
		{
			class CompressStyleRunVisitor : public Object, public DocumentRun::IVisitor
			{
			public:
				DocumentModel*							model;
				List<DocumentModel::ResolvedStyle>		resolvedStyles;
				List<Ptr<DocumentRun>>					replacedRuns;

				CompressStyleRunVisitor(DocumentModel* _model)
					:model(_model)
				{
					DocumentModel::ResolvedStyle resolvedStyle;
					resolvedStyle = model->GetStyle(DocumentModel::DefaultStyleName, resolvedStyle);
					resolvedStyles.Add(resolvedStyle);
				}

				const DocumentModel::ResolvedStyle& GetCurrentResolvedStyle()
				{
					return resolvedStyles[resolvedStyles.Count() - 1];
				}

				void VisitContainer(DocumentContainerRun* run)
				{
					for (vint i = 0; i < run->runs.Count(); i++)
					{
						Ptr<DocumentRun> subRun = run->runs[i];
						replacedRuns.Clear();
						subRun->Accept(this);
						if (replacedRuns.Count() > 0)
						{
							run->runs.RemoveAt(i);
							for (vint j = 0; j < replacedRuns.Count(); j++)
							{
								run->runs.Insert(i + j, replacedRuns[j]);
							}
							i--;
						}
					}
				}

				void Visit(DocumentTextRun* run)override
				{
				}

				bool OnlyImageOrObject(DocumentContainerRun* run)
				{
					bool onlyImageOrObject = true;
					FOREACH(Ptr<DocumentRun>, subRun, run->runs)
					{
						if (!subRun.Cast<DocumentImageRun>() && !subRun.Cast<DocumentEmbeddedObjectRun>())
						{
							onlyImageOrObject = false;
							break;
						}
					}
					return onlyImageOrObject;
				}

				void Visit(DocumentStylePropertiesRun* run)override
				{
					if (OnlyImageOrObject(run))
					{
						CopyFrom(replacedRuns, run->runs);
						return;
					}

					const DocumentModel::ResolvedStyle& currentResolvedStyle = GetCurrentResolvedStyle();
					DocumentModel::ResolvedStyle resolvedStyle = model->GetStyle(run->style, currentResolvedStyle);

					if (currentResolvedStyle.style.fontFamily			== resolvedStyle.style.fontFamily)			run->style->face =				Nullable<WString>();
					if (currentResolvedStyle.style.size					== resolvedStyle.style.size)				run->style->size =				Nullable<DocumentFontSize>();
					if (currentResolvedStyle.color						== resolvedStyle.color)						run->style->color =				Nullable<Color>();
					if (currentResolvedStyle.backgroundColor			== resolvedStyle.backgroundColor)			run->style->backgroundColor =	Nullable<Color>();
					if (currentResolvedStyle.style.bold					== resolvedStyle.style.bold)				run->style->bold =				Nullable<bool>();
					if (currentResolvedStyle.style.italic				== resolvedStyle.style.italic)				run->style->italic =			Nullable<bool>();
					if (currentResolvedStyle.style.underline			== resolvedStyle.style.underline)			run->style->underline =			Nullable<bool>();
					if (currentResolvedStyle.style.strikeline			== resolvedStyle.style.strikeline)			run->style->strikeline =		Nullable<bool>();
					if (currentResolvedStyle.style.antialias			== resolvedStyle.style.antialias)			run->style->antialias =			Nullable<bool>();
					if (currentResolvedStyle.style.verticalAntialias	== resolvedStyle.style.verticalAntialias)	run->style->verticalAntialias =	Nullable<bool>();

					if (run->style->face)				goto CONTINUE_PROCESSING;
					if (run->style->size)				goto CONTINUE_PROCESSING;
					if (run->style->color)				goto CONTINUE_PROCESSING;
					if (run->style->backgroundColor)	goto CONTINUE_PROCESSING;
					if (run->style->bold)				goto CONTINUE_PROCESSING;
					if (run->style->italic)				goto CONTINUE_PROCESSING;
					if (run->style->underline)			goto CONTINUE_PROCESSING;
					if (run->style->strikeline)			goto CONTINUE_PROCESSING;
					if (run->style->antialias)			goto CONTINUE_PROCESSING;
					if (run->style->verticalAntialias)	goto CONTINUE_PROCESSING;
					CopyFrom(replacedRuns, run->runs);
					return;

				CONTINUE_PROCESSING:
					if (From(run->runs).Cast<DocumentStylePropertiesRun>().First(nullptr) != nullptr)
					{
						FOREACH(Ptr<DocumentRun>, subRun, run->runs)
						{
							if (auto styleRun = subRun.Cast<DocumentStylePropertiesRun>())
							{
								DocumentModel::MergeStyle(styleRun->style, run->style);
								replacedRuns.Add(styleRun);
							}
							else
							{
								auto parentRun = CopyRun(run).Cast<DocumentStylePropertiesRun>();
								parentRun->runs.Add(subRun);
								replacedRuns.Add(parentRun);
							}
						}
						return;
					}

					resolvedStyles.Add(resolvedStyle);
					VisitContainer(run);
					resolvedStyles.RemoveAt(resolvedStyles.Count() - 1);
				}

				void Visit(DocumentStyleApplicationRun* run)override
				{
					if (OnlyImageOrObject(run))
					{
						CopyFrom(replacedRuns, run->runs);
						return;
					}

					const DocumentModel::ResolvedStyle& currentResolvedStyle = GetCurrentResolvedStyle();
					DocumentModel::ResolvedStyle resolvedStyle = model->GetStyle(run->styleName, currentResolvedStyle);
					resolvedStyles.Add(resolvedStyle);
					VisitContainer(run);
					resolvedStyles.RemoveAt(resolvedStyles.Count() - 1);
				}

				void Visit(DocumentHyperlinkRun* run)override
				{
					const DocumentModel::ResolvedStyle& currentResolvedStyle = GetCurrentResolvedStyle();
					DocumentModel::ResolvedStyle resolvedStyle = model->GetStyle(run->styleName, currentResolvedStyle);
					resolvedStyles.Add(resolvedStyle);
					VisitContainer(run);
					resolvedStyles.RemoveAt(resolvedStyles.Count() - 1);
				}

				void Visit(DocumentImageRun* run)override
				{
				}

				void Visit(DocumentEmbeddedObjectRun* run)override
				{
				}

				void Visit(DocumentParagraphRun* run)override
				{
					VisitContainer(run);
				}
			};
		}
		using namespace document_operation_visitors;

/***********************************************************************
Merge sibling runs if they are exactly the same
***********************************************************************/

		namespace document_operation_visitors
		{
			class MergeSiblingRunVisitor : public Object, public DocumentRun::IVisitor
			{
			public:
				Ptr<DocumentRun>						nextRun;
				Ptr<DocumentRun>						replacedRun;

				void Visit(DocumentTextRun* run)override
				{
					if (auto sibilingRun = nextRun.Cast<DocumentTextRun>())
					{
						run->text += sibilingRun->text;
						replacedRun = run;
					}
				}

				void Visit(DocumentStylePropertiesRun* run)override
				{
					if (auto sibilingRun = nextRun.Cast<DocumentStylePropertiesRun>())
					{
						if (run->style->face !=					sibilingRun->style->face)				return;
						if (run->style->size !=					sibilingRun->style->size)				return;
						if (run->style->color !=				sibilingRun->style->color)				return;
						if (run->style->backgroundColor !=		sibilingRun->style->backgroundColor)	return;
						if (run->style->bold !=					sibilingRun->style->bold)				return;
						if (run->style->italic !=				sibilingRun->style->italic)				return;
						if (run->style->underline !=			sibilingRun->style->underline)			return;
						if (run->style->strikeline !=			sibilingRun->style->strikeline)			return;
						if (run->style->antialias !=			sibilingRun->style->antialias)			return;
						if (run->style->verticalAntialias !=	sibilingRun->style->verticalAntialias)	return;

						CopyFrom(run->runs, sibilingRun->runs, true);
						replacedRun = run;
					}
				}

				void Visit(DocumentStyleApplicationRun* run)override
				{
					if (auto sibilingRun = nextRun.Cast<DocumentStyleApplicationRun>())
					{
						if (run->styleName == sibilingRun->styleName)
						{
							CopyFrom(run->runs, sibilingRun->runs, true);
							replacedRun = run;
						}
					}
				}

				void Visit(DocumentHyperlinkRun* run)override
				{
					if (auto sibilingRun = nextRun.Cast<DocumentHyperlinkRun>())
					{
						if (run->styleName == sibilingRun->styleName &&
							run->normalStyleName == sibilingRun->normalStyleName &&
							run->activeStyleName == sibilingRun->activeStyleName &&
							run->reference == sibilingRun->reference)
						{
							CopyFrom(run->runs, sibilingRun->runs, true);
							replacedRun = run;
						}
					}
				}

				void Visit(DocumentImageRun* run)override
				{
				}

				void Visit(DocumentEmbeddedObjectRun* run)override
				{
				}

				void Visit(DocumentParagraphRun* run)override
				{
				}
			};

			class MergeSiblingRunRecursivelyVisitor : public Object, public DocumentRun::IVisitor
			{
			public:
				Ptr<DocumentRun>						replacedRun;
				Ptr<DocumentRun>						nextRun;

				void VisitContainer(DocumentContainerRun* run)
				{
					for (vint i = 0; i < run->runs.Count() - 1; i++)
					{
						auto currentRun = run->runs[i];
						auto nextRun = run->runs[i + 1];

						MergeSiblingRunVisitor visitor;
						visitor.nextRun = nextRun;
						currentRun->Accept(&visitor);

						if (visitor.replacedRun)
						{
							run->runs.RemoveAt(i + 1);
							run->runs[i] = visitor.replacedRun;
							i--;
						}
					}

					for (vint i = 0; i < run->runs.Count() - 1; i++)
					{
						run->runs[i]->Accept(this);
					}
				}

				void Visit(DocumentTextRun* run)override
				{
				}

				void Visit(DocumentStylePropertiesRun* run)override
				{
					VisitContainer(run);
				}

				void Visit(DocumentStyleApplicationRun* run)override
				{
					VisitContainer(run);
				}

				void Visit(DocumentHyperlinkRun* run)override
				{
					VisitContainer(run);
				}

				void Visit(DocumentImageRun* run)override
				{
				}

				void Visit(DocumentEmbeddedObjectRun* run)override
				{
				}

				void Visit(DocumentParagraphRun* run)override
				{
					VisitContainer(run);
				}
			};
		}
		using namespace document_operation_visitors;

		namespace document_editor
		{
			void ClearUnnecessaryRun(DocumentParagraphRun* run, DocumentModel* model)
			{
				{
					ClearRunVisitor visitor;
					run->Accept(&visitor);
				}
				{
					CompressStyleRunVisitor visitor(model);
					run->Accept(&visitor);
				}
				{
					MergeSiblingRunRecursivelyVisitor visitor;
					run->Accept(&visitor);
				}
			}
		}
	}
}

/***********************************************************************
.\RESOURCES\GUIDOCUMENTEDITOR_CLONERUN.CPP
***********************************************************************/

namespace vl
{
	namespace presentation
	{
		using namespace collections;
		using namespace document_editor;

/***********************************************************************
Clone the current run without its children
If clonedRun field is assigned then it will be added to the cloned container run
***********************************************************************/

		namespace document_operation_visitors
		{
			class CloneRunVisitor : public Object, public DocumentRun::IVisitor
			{
			public:
				Ptr<DocumentRun>				clonedRun;

				CloneRunVisitor(Ptr<DocumentRun> subRun)
					:clonedRun(subRun)
				{
				}

				void VisitContainer(Ptr<DocumentContainerRun> cloned)
				{
					if (clonedRun)
					{
						cloned->runs.Add(clonedRun);
					}
					clonedRun = cloned;
				}

				void Visit(DocumentTextRun* run)override
				{
					Ptr<DocumentTextRun> cloned = new DocumentTextRun;
					cloned->text = run->text;
					clonedRun = cloned;
				}

				void Visit(DocumentStylePropertiesRun* run)override
				{
					Ptr<DocumentStylePropertiesRun> cloned = new DocumentStylePropertiesRun;
					cloned->style = CopyStyle(run->style);
					VisitContainer(cloned);
				}

				void Visit(DocumentStyleApplicationRun* run)override
				{
					Ptr<DocumentStyleApplicationRun> cloned = new DocumentStyleApplicationRun;
					cloned->styleName = run->styleName;

					VisitContainer(cloned);
				}

				void Visit(DocumentHyperlinkRun* run)override
				{
					Ptr<DocumentHyperlinkRun> cloned = new DocumentHyperlinkRun;
					cloned->styleName = run->styleName;
					cloned->normalStyleName = run->normalStyleName;
					cloned->activeStyleName = run->activeStyleName;
					cloned->reference = run->reference;

					VisitContainer(cloned);
				}

				void Visit(DocumentImageRun* run)override
				{
					Ptr<DocumentImageRun> cloned = new DocumentImageRun;
					cloned->size = run->size;
					cloned->baseline = run->baseline;
					cloned->image = run->image;
					cloned->frameIndex = run->frameIndex;
					cloned->source = run->source;
					clonedRun = cloned;
				}

				void Visit(DocumentEmbeddedObjectRun* run)override
				{
					Ptr<DocumentEmbeddedObjectRun> cloned = new DocumentEmbeddedObjectRun;
					cloned->name = run->name;
					clonedRun = cloned;
				}

				void Visit(DocumentParagraphRun* run)override
				{
					Ptr<DocumentParagraphRun> cloned = new DocumentParagraphRun;
					cloned->alignment = run->alignment;

					VisitContainer(cloned);
				}
			};
		}
		using namespace document_operation_visitors;

/***********************************************************************
Clone the current run with its children
***********************************************************************/

		namespace document_operation_visitors
		{
			class CloneRunRecursivelyVisitor : public Object, public DocumentRun::IVisitor
			{
			public:
				Ptr<DocumentRun>				clonedRun;
				RunRangeMap&					runRanges;
				vint							start;
				vint							end;
				bool							deepCopy;

				CloneRunRecursivelyVisitor(RunRangeMap& _runRanges, vint _start, vint _end, bool _deepCopy)
					:runRanges(_runRanges)
					, start(_start)
					, end(_end)
					, deepCopy(_deepCopy)
				{
				}

				void VisitContainer(DocumentContainerRun* run)
				{
					clonedRun = 0;
					RunRange range = runRanges[run];
					if (range.start <= end && start <= range.end)
					{
						if (start <= range.start && range.end <= end && !deepCopy)
						{
							clonedRun = run;
						}
						else
						{
							Ptr<DocumentContainerRun> containerRun = CopyRun(run).Cast<DocumentContainerRun>();
							FOREACH(Ptr<DocumentRun>, subRun, run->runs)
							{
								subRun->Accept(this);
								if (clonedRun)
								{
									containerRun->runs.Add(clonedRun);
								}
							}
							clonedRun = containerRun;
						}
					}
				}

				void Visit(DocumentTextRun* run)override
				{
					clonedRun = 0;
					RunRange range = runRanges[run];
					if (range.start<end && start<range.end)
					{
						if (start <= range.start && range.end <= end)
						{
							if (deepCopy)
							{
								clonedRun = CopyRun(run);
							}
							else
							{
								clonedRun = run;
							}
						}
						else
						{
							Ptr<DocumentTextRun> textRun = new DocumentTextRun;
							vint copyStart = start>range.start ? start : range.start;
							vint copyEnd = end<range.end ? end : range.end;
							if (copyStart<copyEnd)
							{
								textRun->text = run->text.Sub(copyStart - range.start, copyEnd - copyStart);
							}
							clonedRun = textRun;
						}
					}
				}

				void Visit(DocumentStylePropertiesRun* run)override
				{
					VisitContainer(run);
				}

				void Visit(DocumentStyleApplicationRun* run)override
				{
					VisitContainer(run);
				}

				void Visit(DocumentHyperlinkRun* run)override
				{
					VisitContainer(run);
				}

				void Visit(DocumentImageRun* run)override
				{
					clonedRun = 0;
					RunRange range = runRanges[run];
					if (range.start<end && start<range.end)
					{
						if (deepCopy)
						{
							clonedRun = CopyRun(run);
						}
						else
						{
							clonedRun = run;
						}
					}
				}

				void Visit(DocumentEmbeddedObjectRun* run)override
				{
					clonedRun = 0;
					RunRange range = runRanges[run];
					if (range.start<end && start<range.end)
					{
						if (deepCopy)
						{
							clonedRun = CopyRun(run);
						}
						else
						{
							clonedRun = run;
						}
					}
				}

				void Visit(DocumentParagraphRun* run)override
				{
					VisitContainer(run);
				}
			};
		}
		using namespace document_operation_visitors;

		namespace document_editor
		{
			Ptr<DocumentStyleProperties> CopyStyle(Ptr<DocumentStyleProperties> style)
			{
				Ptr<DocumentStyleProperties> newStyle = new DocumentStyleProperties;

				newStyle->face = style->face;
				newStyle->size = style->size;
				newStyle->color = style->color;
				newStyle->backgroundColor = style->backgroundColor;
				newStyle->bold = style->bold;
				newStyle->italic = style->italic;
				newStyle->underline = style->underline;
				newStyle->strikeline = style->strikeline;
				newStyle->antialias = style->antialias;
				newStyle->verticalAntialias = style->verticalAntialias;

				return newStyle;
			}

			Ptr<DocumentRun> CopyRun(DocumentRun* run)
			{
				CloneRunVisitor visitor(0);
				run->Accept(&visitor);
				return visitor.clonedRun;
			}

			Ptr<DocumentRun> CopyStyledText(List<DocumentContainerRun*>& styleRuns, const WString& text)
			{
				Ptr<DocumentTextRun> textRun = new DocumentTextRun;
				textRun->text = text;

				CloneRunVisitor visitor(textRun);
				for (vint i = styleRuns.Count() - 1; i >= 0; i--)
				{
					styleRuns[i]->Accept(&visitor);
				}

				return visitor.clonedRun;
			}

			Ptr<DocumentRun> CopyRunRecursively(DocumentParagraphRun* run, RunRangeMap& runRanges, vint start, vint end, bool deepCopy)
			{
				CloneRunRecursivelyVisitor visitor(runRanges, start, end, deepCopy);
				run->Accept(&visitor);
				return visitor.clonedRun;
			}
		}
	}
}

/***********************************************************************
.\RESOURCES\GUIDOCUMENTEDITOR_COLLECTSTYLE.CPP
***********************************************************************/

namespace vl
{
	namespace presentation
	{
		using namespace collections;

/***********************************************************************
Search all used style names
***********************************************************************/

		namespace document_operation_visitors
		{
			class CollectStyleNameVisitor : public Object, public DocumentRun::IVisitor
			{
			public:
				List<WString>&					styleNames;

				CollectStyleNameVisitor(List<WString>& _styleNames)
					:styleNames(_styleNames)
				{
				}

				void VisitContainer(DocumentContainerRun* run)
				{
					FOREACH(Ptr<DocumentRun>, subRun, run->runs)
					{
						subRun->Accept(this);
					}
				}

				void Visit(DocumentTextRun* run)override
				{
				}

				void Visit(DocumentStylePropertiesRun* run)override
				{
					VisitContainer(run);
				}

				void Visit(DocumentStyleApplicationRun* run)override
				{
					if (!styleNames.Contains(run->styleName))
					{
						styleNames.Add(run->styleName);
					}
					VisitContainer(run);
				}

				void Visit(DocumentHyperlinkRun* run)override
				{
					if (!styleNames.Contains(run->normalStyleName))
					{
						styleNames.Add(run->normalStyleName);
					}
					if (!styleNames.Contains(run->activeStyleName))
					{
						styleNames.Add(run->activeStyleName);
					}
					VisitContainer(run);
				}

				void Visit(DocumentImageRun* run)override
				{
				}

				void Visit(DocumentEmbeddedObjectRun* run)override
				{
				}

				void Visit(DocumentParagraphRun* run)override
				{
					VisitContainer(run);
				}
			};
		}
		using namespace document_operation_visitors;

		namespace document_editor
		{
			void CollectStyleName(DocumentParagraphRun* run, List<WString>& styleNames)
			{
				CollectStyleNameVisitor visitor(styleNames);
				run->Accept(&visitor);
			}
		}
	}
}

/***********************************************************************
.\RESOURCES\GUIDOCUMENTEDITOR_CUTRUN.CPP
***********************************************************************/

namespace vl
{
	namespace presentation
	{
		using namespace collections;
		using namespace document_editor;

/***********************************************************************
Cut all runs into pieces so that a run either completely outside or inside the specified range
If a run decides that itself should be cut, then leftRun and rightRun contains new run that will be inserted before and after it
***********************************************************************/

		namespace document_operation_visitors
		{
			class CutRunVisitor : public Object, public DocumentRun::IVisitor
			{
			public:
				RunRangeMap&					runRanges;
				vint							position;
				Ptr<DocumentRun>				leftRun;
				Ptr<DocumentRun>				rightRun;

				CutRunVisitor(RunRangeMap& _runRanges, vint _position)
					:runRanges(_runRanges)
					, position(_position)
				{
				}

				void VisitContainer(DocumentContainerRun* run)
				{
					vint leftCount = 0;
					Ptr<DocumentRun> selectedRun;

					FOREACH(Ptr<DocumentRun>, subRun, run->runs)
					{
						RunRange range = runRanges[subRun.Obj()];
						if (range.start<position)
						{
							leftCount++;
							if (position<range.end)
							{
								selectedRun = subRun;
							}
						}
						else
						{
							break;
						}
					}

					if (selectedRun)
					{
						selectedRun->Accept(this);
						if (leftRun && rightRun)
						{
							run->runs.RemoveAt(leftCount - 1);
							run->runs.Insert(leftCount - 1, leftRun);
							run->runs.Insert(leftCount, rightRun);
						}
					}

					Ptr<DocumentContainerRun> leftContainer = CopyRun(run).Cast<DocumentContainerRun>();
					Ptr<DocumentContainerRun> rightContainer = CopyRun(run).Cast<DocumentContainerRun>();
					for (vint i = 0; i<run->runs.Count(); i++)
					{
						(i<leftCount ? leftContainer : rightContainer)->runs.Add(run->runs[i]);
					}
					leftRun = leftContainer;
					rightRun = rightContainer;
				}

				void Visit(DocumentTextRun* run)override
				{
					RunRange range = runRanges[run];

					Ptr<DocumentTextRun> leftText = new DocumentTextRun;
					leftText->text = run->text.Sub(0, position - range.start);

					Ptr<DocumentTextRun> rightText = new DocumentTextRun;
					rightText->text = run->text.Sub(position - range.start, range.end - position);

					leftRun = leftText;
					rightRun = rightText;
				}

				void Visit(DocumentStylePropertiesRun* run)override
				{
					VisitContainer(run);
				}

				void Visit(DocumentStyleApplicationRun* run)override
				{
					VisitContainer(run);
				}

				void Visit(DocumentHyperlinkRun* run)override
				{
					VisitContainer(run);
				}

				void Visit(DocumentImageRun* run)override
				{
					leftRun = 0;
					rightRun = 0;
				}

				void Visit(DocumentEmbeddedObjectRun* run)override
				{
					leftRun = 0;
					rightRun = 0;
				}

				void Visit(DocumentParagraphRun* run)override
				{
					VisitContainer(run);
				}
			};
		}
		using namespace document_operation_visitors;

		namespace document_editor
		{
			void CutRun(DocumentParagraphRun* run, RunRangeMap& runRanges, vint position, Ptr<DocumentRun>& leftRun, Ptr<DocumentRun>& rightRun)
			{
				CutRunVisitor visitor(runRanges, position);
				run->Accept(&visitor);
				leftRun = visitor.leftRun;
				rightRun = visitor.rightRun;
			}
		}
	}
}

/***********************************************************************
.\RESOURCES\GUIDOCUMENTEDITOR_GETRUNRANGE.CPP
***********************************************************************/

namespace vl
{
	namespace presentation
	{
		using namespace collections;

/***********************************************************************
Calculate range informations for each run object
***********************************************************************/

		namespace document_operation_visitors
		{
			class GetRunRangeVisitor : public Object, public DocumentRun::IVisitor
			{
			public:
				RunRangeMap&					runRanges;
				vint							start;

				GetRunRangeVisitor(RunRangeMap& _runRanges)
					:runRanges(_runRanges)
					, start(0)
				{
				}

				void VisitContainer(DocumentContainerRun* run)
				{
					RunRange range;
					range.start = start;
					FOREACH(Ptr<DocumentRun>, subRun, run->runs)
					{
						subRun->Accept(this);
					}
					range.end = start;
					runRanges.Add(run, range);
				}

				void VisitContent(DocumentContentRun* run)
				{
					RunRange range;
					range.start = start;
					start += run->GetRepresentationText().Length();
					range.end = start;
					runRanges.Add(run, range);
				}

				void Visit(DocumentTextRun* run)override
				{
					VisitContent(run);
				}

				void Visit(DocumentStylePropertiesRun* run)override
				{
					VisitContainer(run);
				}

				void Visit(DocumentStyleApplicationRun* run)override
				{
					VisitContainer(run);
				}

				void Visit(DocumentHyperlinkRun* run)override
				{
					VisitContainer(run);
				}

				void Visit(DocumentImageRun* run)override
				{
					VisitContent(run);
				}

				void Visit(DocumentEmbeddedObjectRun* run)override
				{
					VisitContent(run);
				}

				void Visit(DocumentParagraphRun* run)override
				{
					VisitContainer(run);
				}
			};
		}
		using namespace document_operation_visitors;

		namespace document_editor
		{
			void GetRunRange(DocumentParagraphRun* run, RunRangeMap& runRanges)
			{
				GetRunRangeVisitor visitor(runRanges);
				run->Accept(&visitor);
			}
		}
	}
}

/***********************************************************************
.\RESOURCES\GUIDOCUMENTEDITOR_LOCALEHYPERLINK.CPP
***********************************************************************/

namespace vl
{
	namespace presentation
	{
		using namespace collections;

/***********************************************************************
Get the hyperlink run that contains the specified position
***********************************************************************/

		namespace document_operation_visitors
		{
			class LocateHyperlinkVisitor : public Object, public DocumentRun::IVisitor
			{
			public:
				Ptr<DocumentHyperlinkRun::Package>	package;
				RunRangeMap&						runRanges;
				vint								start;
				vint								end;

				LocateHyperlinkVisitor(RunRangeMap& _runRanges, Ptr<DocumentHyperlinkRun::Package> _package, vint _start, vint _end)
					:runRanges(_runRanges)
					, package(_package)
					, start(_start)
					, end(_end)
				{
				}

				void VisitContainer(DocumentContainerRun* run)
				{
					Ptr<DocumentRun> selectedRun;
					FOREACH(Ptr<DocumentRun>, subRun, run->runs)
					{
						RunRange range = runRanges[subRun.Obj()];
						if (range.start <= start && end <= range.end)
						{
							subRun->Accept(this);
							break;
						}
					}
				}

				void Visit(DocumentTextRun* run)override
				{
				}

				void Visit(DocumentStylePropertiesRun* run)override
				{
					VisitContainer(run);
				}

				void Visit(DocumentStyleApplicationRun* run)override
				{
					VisitContainer(run);
				}

				void Visit(DocumentHyperlinkRun* run)override
				{
					package->hyperlinks.Add(run);
				}

				void Visit(DocumentImageRun* run)override
				{
				}

				void Visit(DocumentEmbeddedObjectRun* run)override
				{
				}

				void Visit(DocumentParagraphRun* run)override
				{
					VisitContainer(run);
				}
			};
		}
		using namespace document_operation_visitors;

		namespace document_editor
		{
			Ptr<DocumentHyperlinkRun::Package> LocateHyperlink(DocumentParagraphRun* run, RunRangeMap& runRanges, vint row, vint start, vint end)
			{
				auto package = MakePtr<DocumentHyperlinkRun::Package>();
				package->row = row;
				{
					LocateHyperlinkVisitor visitor(runRanges, package, start, end);
					run->Accept(&visitor);
				}

				Ptr<DocumentHyperlinkRun> startRun, endRun;
				FOREACH(Ptr<DocumentHyperlinkRun>, run, package->hyperlinks)
				{
					auto range = runRanges[run.Obj()];
					if (package->start == -1 || range.start < package->start)
					{
						package->start = range.start;
						startRun = run;
					}
					if (package->end == -1 || range.end > package->end)
					{
						package->end = range.end;
						endRun = run;
					}
				}

				while (startRun)
				{
					vint pos = runRanges[startRun.Obj()].start;
					if (pos == 0) break;

					auto newPackage = MakePtr<DocumentHyperlinkRun::Package>();
					LocateHyperlinkVisitor visitor(runRanges, newPackage, pos - 1, pos);
					run->Accept(&visitor);
					if (newPackage->hyperlinks.Count() == 0) break;

					auto newRun = newPackage->hyperlinks[0];
					if (startRun->reference != newRun->reference) break;

					auto range = runRanges[newRun.Obj()];
					package->hyperlinks.Add(newRun);
					package->start = range.start;
					startRun = newRun;
				}

				vint length = runRanges[run].end;
				while (endRun)
				{
					vint pos = runRanges[endRun.Obj()].end;
					if (pos == length) break;

					auto newPackage = MakePtr<DocumentHyperlinkRun::Package>();
					LocateHyperlinkVisitor visitor(runRanges, newPackage, pos, pos + 1);
					run->Accept(&visitor);
					if (newPackage->hyperlinks.Count() == 0) break;

					auto newRun = newPackage->hyperlinks[0];
					if (endRun->reference != newRun->reference) break;

					auto range = runRanges[newRun.Obj()];
					package->hyperlinks.Add(newRun);
					package->end = range.end;
					endRun = newRun;
				}

				return package;
			}
		}
	}
}

/***********************************************************************
.\RESOURCES\GUIDOCUMENTEDITOR_LOCALESTYLE.CPP
***********************************************************************/

namespace vl
{
	namespace presentation
	{
		using namespace collections;

/***********************************************************************
Get all container runs that contain the specified position from top to bottom
***********************************************************************/

		namespace document_operation_visitors
		{
			class LocateStyleVisitor : public Object, public DocumentRun::IVisitor
			{
			public:
				List<DocumentContainerRun*>&	locatedRuns;
				RunRangeMap&					runRanges;
				vint							position;
				bool							frontSide;

				LocateStyleVisitor(List<DocumentContainerRun*>& _locatedRuns, RunRangeMap& _runRanges, vint _position, bool _frontSide)
					:locatedRuns(_locatedRuns)
					, runRanges(_runRanges)
					, position(_position)
					, frontSide(_frontSide)
				{
				}

				void VisitContainer(DocumentContainerRun* run)
				{
					locatedRuns.Add(run);
					Ptr<DocumentRun> selectedRun;
					FOREACH(Ptr<DocumentRun>, subRun, run->runs)
					{
						RunRange range = runRanges[subRun.Obj()];
						if (position == range.start)
						{
							if (!frontSide)
							{
								selectedRun = subRun;
								break;
							}
						}
						else if (position == range.end)
						{
							if (frontSide)
							{
								selectedRun = subRun;
								break;
							}
						}
						else if (range.start<position && position<range.end)
						{
							selectedRun = subRun;
							break;
						}
					}

					if (selectedRun)
					{
						selectedRun->Accept(this);
					}
				}

				void Visit(DocumentTextRun* run)override
				{
				}

				void Visit(DocumentStylePropertiesRun* run)override
				{
					VisitContainer(run);
				}

				void Visit(DocumentStyleApplicationRun* run)override
				{
					VisitContainer(run);
				}

				void Visit(DocumentHyperlinkRun* run)override
				{
					VisitContainer(run);
				}

				void Visit(DocumentImageRun* run)override
				{
				}

				void Visit(DocumentEmbeddedObjectRun* run)override
				{
				}

				void Visit(DocumentParagraphRun* run)override
				{
					VisitContainer(run);
				}
			};
		}
		using namespace document_operation_visitors;

		namespace document_editor
		{
			void LocateStyle(DocumentParagraphRun* run, RunRangeMap& runRanges, vint position, bool frontSide, List<DocumentContainerRun*>& locatedRuns)
			{
				LocateStyleVisitor visitor(locatedRuns, runRanges, position, frontSide);
				run->Accept(&visitor);
			}
		}
	}
}

/***********************************************************************
.\RESOURCES\GUIDOCUMENTEDITOR_REMOVECONTAINER.CPP
***********************************************************************/

namespace vl
{
	namespace presentation
	{
		using namespace collections;

/***********************************************************************
Remove some containers that intersect with the specified range
If a run decides that itself should be removed, then replacedRuns contains all runs to replace itself
RemoveHyperlinkVisitor	: Remove all hyperlinks that intersect with the specified range
RemoveStyleNameVisitor	: Remove all style names that intersect with the specified range
ClearStyleVisitor		: Remove all styles that intersect with the specified range
***********************************************************************/

		namespace document_operation_visitors
		{
			class RemoveContainerVisitor : public Object, public DocumentRun::IVisitor
			{
			public:
				RunRangeMap&					runRanges;
				vint							start;
				vint							end;
				List<Ptr<DocumentRun>>			replacedRuns;

				RemoveContainerVisitor(RunRangeMap& _runRanges, vint _start, vint _end)
					:runRanges(_runRanges)
					, start(_start)
					, end(_end)
				{
				}

				void VisitContainer(DocumentContainerRun* run)
				{
					for (vint i = run->runs.Count() - 1; i >= 0; i--)
					{
						Ptr<DocumentRun> subRun = run->runs[i];
						RunRange range = runRanges[subRun.Obj()];
						if (range.start<end && start<range.end)
						{
							replacedRuns.Clear();
							subRun->Accept(this);
							if (replacedRuns.Count() != 1 || replacedRuns[0] != subRun)
							{
								run->runs.RemoveAt(i);
								for (vint j = 0; j<replacedRuns.Count(); j++)
								{
									run->runs.Insert(i + j, replacedRuns[j]);
								}
								i += replacedRuns.Count();
							}
						}
					}
					replacedRuns.Clear();
					replacedRuns.Add(run);
				}

				void VisitContent(DocumentContentRun* run)
				{
					replacedRuns.Add(run);
				}

				void RemoveContainer(DocumentContainerRun* run)
				{
					CopyFrom(replacedRuns, run->runs);
				}

				void Visit(DocumentTextRun* run)override
				{
					VisitContent(run);
				}

				void Visit(DocumentStylePropertiesRun* run)override
				{
					VisitContainer(run);
				}

				void Visit(DocumentStyleApplicationRun* run)override
				{
					VisitContainer(run);
				}

				void Visit(DocumentHyperlinkRun* run)override
				{
					VisitContainer(run);
				}

				void Visit(DocumentImageRun* run)override
				{
					VisitContent(run);
				}

				void Visit(DocumentEmbeddedObjectRun* run)override
				{
					VisitContent(run);
				}

				void Visit(DocumentParagraphRun* run)override
				{
					VisitContainer(run);
				}
			};

			class RemoveHyperlinkVisitor : public RemoveContainerVisitor
			{
			public:
				RemoveHyperlinkVisitor(RunRangeMap& _runRanges, vint _start, vint _end)
					:RemoveContainerVisitor(_runRanges, _start, _end)
				{
				}

				void Visit(DocumentHyperlinkRun* run)override
				{
					RemoveContainer(run);
				}
			};

			class RemoveStyleNameVisitor : public RemoveContainerVisitor
			{
			public:
				RemoveStyleNameVisitor(RunRangeMap& _runRanges, vint _start, vint _end)
					:RemoveContainerVisitor(_runRanges, _start, _end)
				{
				}

				void Visit(DocumentStyleApplicationRun* run)override
				{
					RemoveContainer(run);
				}
			};

			class ClearStyleVisitor : public RemoveContainerVisitor
			{
			public:
				ClearStyleVisitor(RunRangeMap& _runRanges, vint _start, vint _end)
					:RemoveContainerVisitor(_runRanges, _start, _end)
				{
				}

				void Visit(DocumentStylePropertiesRun* run)override
				{
					RemoveContainer(run);
				}

				void Visit(DocumentStyleApplicationRun* run)override
				{
					RemoveContainer(run);
				}
			};
		}
		using namespace document_operation_visitors;

		namespace document_editor
		{
			void RemoveHyperlink(DocumentParagraphRun* run, RunRangeMap& runRanges, vint start, vint end)
			{
				RemoveHyperlinkVisitor visitor(runRanges, start, end);
				run->Accept(&visitor);
			}

			void RemoveStyleName(DocumentParagraphRun* run, RunRangeMap& runRanges, vint start, vint end)
			{
				RemoveStyleNameVisitor visitor(runRanges, start, end);
				run->Accept(&visitor);
			}

			void ClearStyle(DocumentParagraphRun* run, RunRangeMap& runRanges, vint start, vint end)
			{
				ClearStyleVisitor visitor(runRanges, start, end);
				run->Accept(&visitor);
			}
		}
	}
}

/***********************************************************************
.\RESOURCES\GUIDOCUMENTEDITOR_REMOVERUN.CPP
***********************************************************************/

namespace vl
{
	namespace presentation
	{
		using namespace collections;

/***********************************************************************
Remove text run contents with the specified range, or other content runs that intersect with the range
If a run decides that itself should be removed, then replacedRuns contains all runs to replace itself
***********************************************************************/

		namespace document_operation_visitors
		{
			class RemoveRunVisitor : public Object, public DocumentRun::IVisitor
			{
			public:
				RunRangeMap&					runRanges;
				vint							start;
				vint							end;
				List<Ptr<DocumentRun>>			replacedRuns;

				RemoveRunVisitor(RunRangeMap& _runRanges, vint _start, vint _end)
					:runRanges(_runRanges)
					, start(_start)
					, end(_end)
				{
				}

				void VisitContainer(DocumentContainerRun* run)
				{
					if (start == end) return;
					for (vint i = run->runs.Count() - 1; i >= 0; i--)
					{
						Ptr<DocumentRun> subRun = run->runs[i];
						RunRange range = runRanges[subRun.Obj()];

						if (range.start <= end && start <= range.end)
						{
							subRun->Accept(this);
							if (replacedRuns.Count() == 0 || subRun != replacedRuns[0])
							{
								run->runs.RemoveAt(i);
								for (vint j = 0; j<replacedRuns.Count(); j++)
								{
									run->runs.Insert(i + j, replacedRuns[j]);
								}
							}
						}
					}
					replacedRuns.Clear();
					replacedRuns.Add(run);
				}

				void Visit(DocumentTextRun* run)override
				{
					replacedRuns.Clear();
					RunRange range = runRanges[run];

					if (start <= range.start)
					{
						if (end<range.end)
						{
							run->text = run->text.Sub(end - range.start, range.end - end);
							replacedRuns.Add(run);
						}
					}
					else
					{
						if (end<range.end)
						{
							DocumentTextRun* firstRun = new DocumentTextRun;
							DocumentTextRun* secondRun = new DocumentTextRun;

							firstRun->text = run->text.Sub(0, start - range.start);
							secondRun->text = run->text.Sub(end - range.start, range.end - end);

							replacedRuns.Add(firstRun);
							replacedRuns.Add(secondRun);
						}
						else
						{
							run->text = run->text.Sub(0, start - range.start);
							replacedRuns.Add(run);
						}
					}
				}

				void Visit(DocumentStylePropertiesRun* run)override
				{
					VisitContainer(run);
				}

				void Visit(DocumentStyleApplicationRun* run)override
				{
					VisitContainer(run);
				}

				void Visit(DocumentHyperlinkRun* run)override
				{
					VisitContainer(run);
				}

				void Visit(DocumentImageRun* run)override
				{
					replacedRuns.Clear();
				}

				void Visit(DocumentEmbeddedObjectRun* run)override
				{
					replacedRuns.Clear();
				}

				void Visit(DocumentParagraphRun* run)override
				{
					VisitContainer(run);
				}
			};
		}
		using namespace document_operation_visitors;

		namespace document_editor
		{
			void RemoveRun(DocumentParagraphRun* run, RunRangeMap& runRanges, vint start, vint end)
			{
				RemoveRunVisitor visitor(runRanges, start, end);
				run->Accept(&visitor);
			}
		}
	}
}

/***********************************************************************
.\RESOURCES\GUIDOCUMENTEDITOR_REPLACESTYLENAME.CPP
***********************************************************************/

namespace vl
{
	namespace presentation
	{
		using namespace collections;

/***********************************************************************
Replace a style name with another one
***********************************************************************/

		namespace document_operation_visitors
		{
			class ReplaceStyleNameVisitor : public Object, public DocumentRun::IVisitor
			{
			public:
				WString							oldStyleName;
				WString							newStyleName;

				ReplaceStyleNameVisitor(const WString& _oldStyleName, const WString& _newStyleName)
					:oldStyleName(_oldStyleName)
					, newStyleName(_newStyleName)
				{
				}

				void VisitContainer(DocumentContainerRun* run)
				{
					FOREACH(Ptr<DocumentRun>, subRun, run->runs)
					{
						subRun->Accept(this);
					}
				}

				void Visit(DocumentTextRun* run)override
				{
				}

				void Visit(DocumentStylePropertiesRun* run)override
				{
					VisitContainer(run);
				}

				void Visit(DocumentStyleApplicationRun* run)override
				{
					if (run->styleName == oldStyleName) run->styleName = newStyleName;
					VisitContainer(run);
				}

				void Visit(DocumentHyperlinkRun* run)override
				{
					if (run->styleName == oldStyleName) run->styleName = newStyleName;
					if (run->normalStyleName == oldStyleName) run->normalStyleName = newStyleName;
					if (run->activeStyleName == oldStyleName) run->activeStyleName = newStyleName;
					VisitContainer(run);
				}

				void Visit(DocumentImageRun* run)override
				{
				}

				void Visit(DocumentEmbeddedObjectRun* run)override
				{
				}

				void Visit(DocumentParagraphRun* run)override
				{
					VisitContainer(run);
				}
			};
		}
		using namespace document_operation_visitors;

		namespace document_editor
		{
			void ReplaceStyleName(DocumentParagraphRun* run, const WString& oldStyleName, const WString& newStyleName)
			{
				ReplaceStyleNameVisitor visitor(oldStyleName, newStyleName);
				run->Accept(&visitor);
			}
		}
	}
}

/***********************************************************************
.\RESOURCES\GUIDOCUMENTEDITOR_SUMMERIZESTYLE.CPP
***********************************************************************/

namespace vl
{
	namespace presentation
	{
		using namespace collections;

		namespace document_operation_visitors
		{

/***********************************************************************
Calculate if all text in the specified range has some common styles
***********************************************************************/

			class SummarizeStyleVisitor : public Object, public DocumentRun::IVisitor
			{
			public:
				RunRangeMap&							runRanges;
				DocumentModel*							model;
				vint									start;
				vint									end;
				Ptr<DocumentStyleProperties>			style;
				List<DocumentModel::ResolvedStyle>		resolvedStyles;

				SummarizeStyleVisitor(RunRangeMap& _runRanges, DocumentModel* _model, vint _start, vint _end)
					:runRanges(_runRanges)
					, model(_model)
					, start(_start)
					, end(_end)
				{
					DocumentModel::ResolvedStyle resolvedStyle;
					resolvedStyle = model->GetStyle(DocumentModel::DefaultStyleName, resolvedStyle);
					resolvedStyles.Add(resolvedStyle);
				}

				const DocumentModel::ResolvedStyle& GetCurrentResolvedStyle()
				{
					return resolvedStyles[resolvedStyles.Count() - 1];
				}

				// ---------------------------------------------------------

				template<typename T>
				void SetStyleItem(Nullable<T> DocumentStyleProperties::* dstField, T FontProperties::* srcField)
				{
					const DocumentModel::ResolvedStyle& src = GetCurrentResolvedStyle();
					if (style.Obj()->*dstField && (style.Obj()->*dstField).Value() != src.style.*srcField)
					{
						style.Obj()->*dstField = Nullable<T>();
					}
				}

				template<typename T>
				void SetStyleItem(Nullable<T> DocumentStyleProperties::* dstField, T DocumentModel::ResolvedStyle::* srcField)
				{
					const DocumentModel::ResolvedStyle& src = GetCurrentResolvedStyle();
					if (style.Obj()->*dstField && (style.Obj()->*dstField).Value() != src.*srcField)
					{
						style.Obj()->*dstField = Nullable<T>();
					}
				}

				void SetStyleItem(Nullable<DocumentFontSize> DocumentStyleProperties::* dstField, vint FontProperties::* srcField)
				{
					const DocumentModel::ResolvedStyle& src = GetCurrentResolvedStyle();
					if (style.Obj()->*dstField)
					{
						auto dfs = (style.Obj()->*dstField).Value();
						if (dfs.relative || dfs.size != src.style.*srcField)
						{
							style.Obj()->*dstField = Nullable<DocumentFontSize>();
						}
					}
				}

				// ---------------------------------------------------------

				template<typename T>
				void OverrideStyleItem(Nullable<T> DocumentStyleProperties::* dstField, T FontProperties::* srcField)
				{
					const DocumentModel::ResolvedStyle& src = GetCurrentResolvedStyle();
					style.Obj()->*dstField = src.style.*srcField;
				}

				template<typename T>
				void OverrideStyleItem(Nullable<T> DocumentStyleProperties::* dstField, T DocumentModel::ResolvedStyle::* srcField)
				{
					const DocumentModel::ResolvedStyle& src = GetCurrentResolvedStyle();
					style.Obj()->*dstField = src.*srcField;
				}

				void OverrideStyleItem(Nullable<DocumentFontSize> DocumentStyleProperties::* dstField, vint FontProperties::* srcField)
				{
					const DocumentModel::ResolvedStyle& src = GetCurrentResolvedStyle();
					style.Obj()->*dstField = DocumentFontSize((double)(src.style.*srcField), false);
				}

				// ---------------------------------------------------------

				void VisitContainer(DocumentContainerRun* run)
				{
					for (vint i = run->runs.Count() - 1; i >= 0; i--)
					{
						Ptr<DocumentRun> subRun = run->runs[i];
						RunRange range = runRanges[subRun.Obj()];
						if (range.start<end && start<range.end)
						{
							subRun->Accept(this);
						}
					}
				}

				void Visit(DocumentTextRun* run)override
				{
					const DocumentModel::ResolvedStyle& currentResolvedStyle = GetCurrentResolvedStyle();
					if (style)
					{
						SetStyleItem(&DocumentStyleProperties::face, &FontProperties::fontFamily);
						SetStyleItem(&DocumentStyleProperties::size, &FontProperties::size);
						SetStyleItem(&DocumentStyleProperties::color, &DocumentModel::ResolvedStyle::color);
						SetStyleItem(&DocumentStyleProperties::backgroundColor, &DocumentModel::ResolvedStyle::backgroundColor);
						SetStyleItem(&DocumentStyleProperties::bold, &FontProperties::bold);
						SetStyleItem(&DocumentStyleProperties::italic, &FontProperties::italic);
						SetStyleItem(&DocumentStyleProperties::underline, &FontProperties::underline);
						SetStyleItem(&DocumentStyleProperties::strikeline, &FontProperties::strikeline);
						SetStyleItem(&DocumentStyleProperties::antialias, &FontProperties::antialias);
						SetStyleItem(&DocumentStyleProperties::verticalAntialias, &FontProperties::verticalAntialias);
					}
					else
					{
						style = new DocumentStyleProperties;
						OverrideStyleItem(&DocumentStyleProperties::face, &FontProperties::fontFamily);
						OverrideStyleItem(&DocumentStyleProperties::size, &FontProperties::size);
						OverrideStyleItem(&DocumentStyleProperties::color, &DocumentModel::ResolvedStyle::color);
						OverrideStyleItem(&DocumentStyleProperties::backgroundColor, &DocumentModel::ResolvedStyle::backgroundColor);
						OverrideStyleItem(&DocumentStyleProperties::bold, &FontProperties::bold);
						OverrideStyleItem(&DocumentStyleProperties::italic, &FontProperties::italic);
						OverrideStyleItem(&DocumentStyleProperties::underline, &FontProperties::underline);
						OverrideStyleItem(&DocumentStyleProperties::strikeline, &FontProperties::strikeline);
						OverrideStyleItem(&DocumentStyleProperties::antialias, &FontProperties::antialias);
						OverrideStyleItem(&DocumentStyleProperties::verticalAntialias, &FontProperties::verticalAntialias);
					}
				}

				void Visit(DocumentStylePropertiesRun* run)override
				{
					const DocumentModel::ResolvedStyle& currentResolvedStyle = GetCurrentResolvedStyle();
					DocumentModel::ResolvedStyle resolvedStyle = model->GetStyle(run->style, currentResolvedStyle);
					resolvedStyles.Add(resolvedStyle);
					VisitContainer(run);
					resolvedStyles.RemoveAt(resolvedStyles.Count() - 1);
				}

				void Visit(DocumentStyleApplicationRun* run)override
				{
					const DocumentModel::ResolvedStyle& currentResolvedStyle = GetCurrentResolvedStyle();
					DocumentModel::ResolvedStyle resolvedStyle = model->GetStyle(run->styleName, currentResolvedStyle);
					resolvedStyles.Add(resolvedStyle);
					VisitContainer(run);
					resolvedStyles.RemoveAt(resolvedStyles.Count() - 1);
				}

				void Visit(DocumentHyperlinkRun* run)override
				{
					const DocumentModel::ResolvedStyle& currentResolvedStyle = GetCurrentResolvedStyle();
					DocumentModel::ResolvedStyle resolvedStyle = model->GetStyle(run->styleName, currentResolvedStyle);
					resolvedStyles.Add(resolvedStyle);
					VisitContainer(run);
					resolvedStyles.RemoveAt(resolvedStyles.Count() - 1);
				}

				void Visit(DocumentImageRun* run)override
				{
				}

				void Visit(DocumentEmbeddedObjectRun* run)override
				{
				}

				void Visit(DocumentParagraphRun* run)override
				{
					VisitContainer(run);
				}
			};

/***********************************************************************
Calculate if all text in the specified range has a common style name
***********************************************************************/

			class SummarizeStyleNameVisitor : public Object, public DocumentRun::IVisitor
			{
			public:
				RunRangeMap&							runRanges;
				DocumentModel*							model;
				vint									start;
				vint									end;
				Nullable<WString>						currentStyleName;
				Nullable<WString>						styleName;
				bool									assignedStyleName = false;

				SummarizeStyleNameVisitor(RunRangeMap& _runRanges, DocumentModel* _model, vint _start, vint _end)
					:runRanges(_runRanges)
					, model(_model)
					, start(_start)
					, end(_end)
				{
				}

				void VisitContentRun(DocumentContentRun* run)
				{
					if (!assignedStyleName)
					{
						styleName = currentStyleName;
						assignedStyleName = true;
					}
					else if (styleName && (!currentStyleName || styleName.Value() != currentStyleName.Value()))
					{
						styleName = {};
					}
				}

				void VisitContainer(DocumentContainerRun* run)
				{
					for (vint i = run->runs.Count() - 1; i >= 0; i--)
					{
						Ptr<DocumentRun> subRun = run->runs[i];
						RunRange range = runRanges[subRun.Obj()];
						if (range.start<end && start<range.end)
						{
							subRun->Accept(this);
						}
					}
				}

				void Visit(DocumentTextRun* run)override
				{
					VisitContentRun(run);
				}

				void Visit(DocumentStylePropertiesRun* run)override
				{
					VisitContainer(run);
				}

				void Visit(DocumentStyleApplicationRun* run)override
				{
					auto oldStyleName = currentStyleName;
					currentStyleName = run->styleName;
					VisitContainer(run);
					currentStyleName = oldStyleName;
				}

				void Visit(DocumentHyperlinkRun* run)override
				{
					VisitContainer(run);
				}

				void Visit(DocumentImageRun* run)override
				{
					VisitContentRun(run);
				}

				void Visit(DocumentEmbeddedObjectRun* run)override
				{
					VisitContentRun(run);
				}

				void Visit(DocumentParagraphRun* run)override
				{
					VisitContainer(run);
				}
			};
		}
		using namespace document_operation_visitors;

		namespace document_editor
		{
			Ptr<DocumentStyleProperties> SummarizeStyle(DocumentParagraphRun* run, RunRangeMap& runRanges, DocumentModel* model, vint start, vint end)
			{
				SummarizeStyleVisitor visitor(runRanges, model, start, end);
				run->Accept(&visitor);
				return visitor.style;
			}

			Nullable<WString> SummarizeStyleName(DocumentParagraphRun* run, RunRangeMap& runRanges, DocumentModel* model, vint start, vint end)
			{
				SummarizeStyleNameVisitor visitor(runRanges, model, start, end);
				run->Accept(&visitor);
				return visitor.styleName;
			}

			template<typename T>
			void AggregateStyleItem(Ptr<DocumentStyleProperties>& dst, Ptr<DocumentStyleProperties> src, Nullable<T> DocumentStyleProperties::* field)
			{
				if (dst.Obj()->*field && (!(src.Obj()->*field) || (dst.Obj()->*field).Value() != (src.Obj()->*field).Value()))
				{
					dst.Obj()->*field = Nullable<T>();
				}
			}

			void AggregateStyle(Ptr<DocumentStyleProperties>& dst, Ptr<DocumentStyleProperties> src)
			{
				AggregateStyleItem(dst, src, &DocumentStyleProperties::face);
				AggregateStyleItem(dst, src, &DocumentStyleProperties::size);
				AggregateStyleItem(dst, src, &DocumentStyleProperties::color);
				AggregateStyleItem(dst, src, &DocumentStyleProperties::backgroundColor);
				AggregateStyleItem(dst, src, &DocumentStyleProperties::bold);
				AggregateStyleItem(dst, src, &DocumentStyleProperties::italic);
				AggregateStyleItem(dst, src, &DocumentStyleProperties::underline);
				AggregateStyleItem(dst, src, &DocumentStyleProperties::strikeline);
				AggregateStyleItem(dst, src, &DocumentStyleProperties::antialias);
				AggregateStyleItem(dst, src, &DocumentStyleProperties::verticalAntialias);
			}
		}
	}
}

/***********************************************************************
.\RESOURCES\GUIDOCUMENT_EDIT.CPP
***********************************************************************/

namespace vl
{
	namespace presentation
	{
		using namespace collections;
		using namespace document_editor;

/***********************************************************************
DocumentModel::EditRangeOperations
***********************************************************************/

		bool DocumentModel::CheckEditRange(TextPos begin, TextPos end, RunRangeMap& relatedRanges)
		{
			// check caret range
			if(begin>end) return false;
			if(begin.row<0 || begin.row>=paragraphs.Count()) return false;
			if(end.row<0 || end.row>=paragraphs.Count()) return false;

			// determine run ranges
			GetRunRange(paragraphs[begin.row].Obj(), relatedRanges);
			if(begin.row!=end.row)
			{
				GetRunRange(paragraphs[end.row].Obj(), relatedRanges);
			}
			
			// check caret range
			RunRange beginRange=relatedRanges[paragraphs[begin.row].Obj()];
			RunRange endRange=relatedRanges[paragraphs[end.row].Obj()];
			if(begin.column<0 || begin.column>beginRange.end) return false;
			if(end.column<0 || end.column>endRange.end) return false;

			return true;
		}

		Ptr<DocumentModel> DocumentModel::CopyDocument(TextPos begin, TextPos end, bool deepCopy)
		{
			// check caret range
			RunRangeMap runRanges;
			if(!CheckEditRange(begin, end, runRanges)) return nullptr;

			// get ranges
			for(vint i=begin.row+1;i<end.row;i++)
			{
				GetRunRange(paragraphs[i].Obj(), runRanges);
			}

			Ptr<DocumentModel> newDocument=new DocumentModel;

			// copy paragraphs
			if(begin.row==end.row)
			{
				newDocument->paragraphs.Add(CopyRunRecursively(paragraphs[begin.row].Obj(), runRanges, begin.column, end.column, deepCopy).Cast<DocumentParagraphRun>());
			}
			else
			{
				for(vint i=begin.row;i<=end.row;i++)
				{
					Ptr<DocumentParagraphRun> paragraph=paragraphs[i];
					RunRange range=runRanges[paragraph.Obj()];
					if(i==begin.row)
					{
						newDocument->paragraphs.Add(CopyRunRecursively(paragraph.Obj(), runRanges, begin.column, range.end, deepCopy).Cast<DocumentParagraphRun>());
					}
					else if(i==end.row)
					{
						newDocument->paragraphs.Add(CopyRunRecursively(paragraph.Obj(), runRanges, range.start, end.column, deepCopy).Cast<DocumentParagraphRun>());
					}
					else if(deepCopy)
					{
						newDocument->paragraphs.Add(CopyRunRecursively(paragraph.Obj(), runRanges, range.start, range.end, deepCopy).Cast<DocumentParagraphRun>());
					}
					else
					{
						newDocument->paragraphs.Add(paragraph);
					}
				}
			}

			// copy styles
			List<WString> styleNames;
			FOREACH(Ptr<DocumentParagraphRun>, paragraph, newDocument->paragraphs)
			{
				CollectStyleName(paragraph.Obj(), styleNames);
			}

			for(vint i=0;i<styleNames.Count();i++)
			{
				WString styleName=styleNames[i];
				if(!newDocument->styles.Keys().Contains(styleName))
				{
					Ptr<DocumentStyle> style=styles[styleName];
					if(deepCopy)
					{
						Ptr<DocumentStyle> newStyle=new DocumentStyle;
						newStyle->parentStyleName=style->parentStyleName;
						newStyle->styles=CopyStyle(style->styles);
						newStyle->resolvedStyles=CopyStyle(style->resolvedStyles);
						newDocument->styles.Add(styleName, newStyle);
					}
					else
					{
						newDocument->styles.Add(styleName, style);
					}

					if(!styleNames.Contains(style->parentStyleName))
					{
						styleNames.Add(style->parentStyleName);
					}
				}
			}

			return newDocument;
		}

		Ptr<DocumentModel> DocumentModel::CopyDocument()
		{
			// determine run ranges
			RunRangeMap runRanges;
			vint lastParagraphIndex = paragraphs.Count() - 1;
			GetRunRange(paragraphs[lastParagraphIndex].Obj(), runRanges);
			
			TextPos begin(0, 0);
			TextPos end(lastParagraphIndex, runRanges[paragraphs[lastParagraphIndex].Obj()].end);
			return CopyDocument(begin, end, true);
		}

		bool DocumentModel::CutParagraph(TextPos position)
		{
			if(position.row<0 || position.row>=paragraphs.Count()) return false;

			Ptr<DocumentParagraphRun> paragraph=paragraphs[position.row];
			RunRangeMap runRanges;
			Ptr<DocumentRun> leftRun, rightRun;

			GetRunRange(paragraph.Obj(), runRanges);
			CutRun(paragraph.Obj(), runRanges, position.column, leftRun, rightRun);

			CopyFrom(paragraph->runs, leftRun.Cast<DocumentParagraphRun>()->runs);
			CopyFrom(paragraph->runs, rightRun.Cast<DocumentParagraphRun>()->runs, true);
			
			return true;
		}

		bool DocumentModel::CutEditRange(TextPos begin, TextPos end)
		{
			// check caret range
			if(begin>end) return false;
			if(begin.row<0 || begin.row>=paragraphs.Count()) return false;
			if(end.row<0 || end.row>=paragraphs.Count()) return false;

			// cut paragraphs
			CutParagraph(begin);
			if(begin!=end)
			{
				CutParagraph(end);
			}
			return true;
		}

		bool DocumentModel::EditContainer(TextPos begin, TextPos end, const Func<void(DocumentParagraphRun*, RunRangeMap&, vint, vint)>& editor)
		{
			if(begin==end) return false;

			// cut paragraphs
			if(!CutEditRange(begin, end)) return false;

			// check caret range
			RunRangeMap runRanges;
			if(!CheckEditRange(begin, end, runRanges)) return false;

			// edit container
			if(begin.row==end.row)
			{
				editor(paragraphs[begin.row].Obj(), runRanges, begin.column, end.column);
			}
			else
			{
				for(vint i=begin.row;i<=end.row;i++)
				{
					Ptr<DocumentParagraphRun> paragraph=paragraphs[i];
					if(begin.row<i && i<end.row)
					{
						GetRunRange(paragraph.Obj(), runRanges);
					}
					RunRange range=runRanges[paragraph.Obj()];
					if(i==begin.row)
					{
						editor(paragraph.Obj(), runRanges, begin.column, range.end);
					}
					else if(i==end.row)
					{
						editor(paragraph.Obj(), runRanges, range.start, end.column);
					}
					else
					{
						editor(paragraph.Obj(), runRanges, range.start, range.end);
					}
				}
			}

			// clear paragraphs
			for(vint i=begin.row;i<=end.row;i++)
			{
				ClearUnnecessaryRun(paragraphs[i].Obj(), this);
			}

			return true;
		}

/***********************************************************************
DocumentModel::EditRun
***********************************************************************/

		vint DocumentModel::EditRun(TextPos begin, TextPos end, Ptr<DocumentModel> replaceToModel, bool copy)
		{
			// check caret range
			RunRangeMap runRanges;
			if(!CheckEditRange(begin, end, runRanges)) return -1;

			auto model = replaceToModel;
			if (copy)
			{
				model = replaceToModel->CopyDocument();
			}

			// calculate new names for the model's styles to prevent conflicting
			List<WString> oldNames, newNames;
			CopyFrom(oldNames, model->styles.Keys());
			CopyFrom(newNames, model->styles.Keys());
			for(vint i=0;i<newNames.Count();i++)
			{
				WString name=newNames[i];
				if((name.Length()==0 || name[0]!=L'#') && styles.Keys().Contains(name))
				{
					vint index=2;
					while(true)
					{
						WString newName=name+L"_"+itow(index++);
						if(!styles.Keys().Contains(newName) && !model->styles.Keys().Contains(newName))
						{
							newNames[i]=newName;
							break;
						}
					}
				}
			}

			// rename model's styles
			typedef Pair<WString, WString> NamePair;
			FOREACH(NamePair, name, From(oldNames).Pairwise(newNames))
			{
				model->RenameStyle(name.key, name.value);
			}
			FOREACH(WString, name, newNames)
			{
				if((name.Length()==0 || name[0]!=L'#') && !styles.Keys().Contains(name))
				{
					styles.Add(name, model->styles[name]);
				}
			}

			// edit runs
			Array<Ptr<DocumentParagraphRun>> runs;
			CopyFrom(runs, model->paragraphs);
			return EditRunNoCopy(begin, end, runs);
		}

		vint DocumentModel::EditRunNoCopy(TextPos begin, TextPos end, const collections::Array<Ptr<DocumentParagraphRun>>& runs)
		{
			// check caret range
			RunRangeMap runRanges;
			if(!CheckEditRange(begin, end, runRanges)) return -1;

			// remove unnecessary paragraphs
			if(begin.row!=end.row)
			{
				for(vint i=end.row-1;i>begin.row;i--)
				{
					paragraphs.RemoveAt(i);
				}
				end.row=begin.row+1;
			}

			// remove unnecessary runs and ensure begin.row!=end.row
			if(begin.row==end.row)
			{
				RemoveRun(paragraphs[begin.row].Obj(), runRanges, begin.column, end.column);

				Ptr<DocumentRun> leftRun, rightRun;
				runRanges.Clear();
				GetRunRange(paragraphs[begin.row].Obj(), runRanges);
				CutRun(paragraphs[begin.row].Obj(), runRanges, begin.column, leftRun, rightRun);

				paragraphs.RemoveAt(begin.row);
				paragraphs.Insert(begin.row, leftRun.Cast<DocumentParagraphRun>());
				paragraphs.Insert(begin.row+1, rightRun.Cast<DocumentParagraphRun>());
				end.row=begin.row+1;
			}
			else
			{
				RemoveRun(paragraphs[begin.row].Obj(), runRanges, begin.column, runRanges[paragraphs[begin.row].Obj()].end);
				RemoveRun(paragraphs[end.row].Obj(), runRanges, 0, end.column);
			}

			// insert new paragraphs
			Ptr<DocumentParagraphRun> beginParagraph=paragraphs[begin.row];
			Ptr<DocumentParagraphRun> endParagraph=paragraphs[end.row];
			if(runs.Count()==0)
			{
				CopyFrom(beginParagraph->runs, endParagraph->runs, true);
				paragraphs.RemoveAt(end.row);
			}
			else if(runs.Count()==1)
			{
				CopyFrom(beginParagraph->runs, runs[0]->runs, true);
				CopyFrom(beginParagraph->runs, endParagraph->runs, true);
				paragraphs.RemoveAt(end.row);
			}
			else
			{
				Ptr<DocumentParagraphRun> newBeginRuns=runs[0];
				CopyFrom(beginParagraph->runs, newBeginRuns->runs, true);
				
				Ptr<DocumentParagraphRun> newEndRuns=runs[runs.Count()-1];
				if (newEndRuns->alignment)
				{
					endParagraph->alignment = newEndRuns->alignment;
				}
				for(vint i=0;i<newEndRuns->runs.Count();i++)
				{
					endParagraph->runs.Insert(i, newEndRuns->runs[i]);
				}

				for(vint i=1;i<runs.Count()-1;i++)
				{
					paragraphs.Insert(begin.row+i, runs[i]);
				}
			}

			// clear unnecessary runs
			vint rows=runs.Count()==0?1:runs.Count();
			for(vint i=0;i<rows;i++)
			{
				ClearUnnecessaryRun(paragraphs[begin.row+i].Obj(), this);
			}
			return rows;
		}

/***********************************************************************
DocumentModel::EditText
***********************************************************************/

		vint DocumentModel::EditText(TextPos begin, TextPos end, bool frontSide, const collections::Array<WString>& text)
		{
			// check caret range
			RunRangeMap runRanges;
			if(!CheckEditRange(begin, end, runRanges)) return -1;

			// calcuate the position to get the text style
			TextPos stylePosition;
			if(frontSide)
			{
				stylePosition=begin;
				if(stylePosition.column==0)
				{
					frontSide=false;
				}
			}
			else
			{
				stylePosition=end;
				if(stylePosition.column==runRanges[paragraphs[end.row].Obj()].end)
				{
					frontSide=true;
				}
			}

			// copy runs that contains the target style for new text
			List<DocumentContainerRun*> styleRuns;
			LocateStyle(paragraphs[stylePosition.row].Obj(), runRanges, stylePosition.column, frontSide, styleRuns);

			// create paragraphs
			Array<Ptr<DocumentParagraphRun>> runs(text.Count());
			for(vint i=0;i<text.Count();i++)
			{
				Ptr<DocumentRun> paragraph=CopyStyledText(styleRuns, text[i]);
				runs[i]=paragraph.Cast<DocumentParagraphRun>();
			}

			// replace the paragraphs
			return EditRunNoCopy(begin, end, runs);
		}

/***********************************************************************
DocumentModel::EditStyle
***********************************************************************/

		bool DocumentModel::EditStyle(TextPos begin, TextPos end, Ptr<DocumentStyleProperties> style)
		{
			return EditContainer(begin, end, [=](DocumentParagraphRun* paragraph, RunRangeMap& runRanges, vint start, vint end)
			{
				AddStyle(paragraph, runRanges, start, end, style);
			});
		}

/***********************************************************************
DocumentModel::EditImage
***********************************************************************/

		Ptr<DocumentImageRun> DocumentModel::EditImage(TextPos begin, TextPos end, Ptr<GuiImageData> image)
		{
			Ptr<DocumentImageRun> imageRun=new DocumentImageRun;
			imageRun->size=image->GetImage()->GetFrame(image->GetFrameIndex())->GetSize();
			imageRun->baseline=imageRun->size.y;
			imageRun->image=image->GetImage();
			imageRun->frameIndex=image->GetFrameIndex();

			Ptr<DocumentParagraphRun> paragraph=new DocumentParagraphRun;
			paragraph->runs.Add(imageRun);

			Array<Ptr<DocumentParagraphRun>> runs(1);
			runs[0]=paragraph;
			if(EditRunNoCopy(begin, end, runs))
			{
				return imageRun;
			}
			else
			{
				return 0;
			}
		}

/***********************************************************************
DocumentModel::EditHyperlink
***********************************************************************/

		bool DocumentModel::EditHyperlink(vint paragraphIndex, vint begin, vint end, const WString& reference, const WString& normalStyleName, const WString& activeStyleName)
		{
			auto package = GetHyperlink(paragraphIndex, begin, end);
			if (package->hyperlinks.Count() > 0)
			{
				FOREACH(Ptr<DocumentHyperlinkRun>, run, package->hyperlinks)
				{
					run->reference = reference;
					run->normalStyleName = normalStyleName;
					run->activeStyleName = activeStyleName;
					run->styleName = normalStyleName;
				}
				return true;
			}
			else if (RemoveHyperlink(paragraphIndex, begin, end))
			{
				CutEditRange(TextPos(paragraphIndex, begin), TextPos(paragraphIndex, end));

				RunRangeMap runRanges;
				Ptr<DocumentParagraphRun> paragraph = paragraphs[paragraphIndex];
				GetRunRange(paragraph.Obj(), runRanges);
				AddHyperlink(paragraph.Obj(), runRanges, begin, end, reference, normalStyleName, activeStyleName);

				ClearUnnecessaryRun(paragraph.Obj(), this);
				return true;
			}
			return false;
		}

		bool DocumentModel::RemoveHyperlink(vint paragraphIndex, vint begin, vint end)
		{
			RunRangeMap runRanges;
			if (!CheckEditRange(TextPos(paragraphIndex, begin), TextPos(paragraphIndex, end), runRanges)) return 0;

			auto paragraph = paragraphs[paragraphIndex];
			auto package = LocateHyperlink(paragraph.Obj(), runRanges, paragraphIndex, begin, end);
			document_editor::RemoveHyperlink(paragraph.Obj(), runRanges, package->start, package->end);
			ClearUnnecessaryRun(paragraph.Obj(), this);
			return true;
		}

		Ptr<DocumentHyperlinkRun::Package> DocumentModel::GetHyperlink(vint paragraphIndex, vint begin, vint end)
		{
			RunRangeMap runRanges;
			if (!CheckEditRange(TextPos(paragraphIndex, begin), TextPos(paragraphIndex, end), runRanges)) return 0;

			auto paragraph = paragraphs[paragraphIndex];
			return LocateHyperlink(paragraph.Obj(), runRanges, paragraphIndex, begin, end);
		}

/***********************************************************************
DocumentModel::EditStyleName
***********************************************************************/

		bool DocumentModel::EditStyleName(TextPos begin, TextPos end, const WString& styleName)
		{
			return EditContainer(begin, end, [=](DocumentParagraphRun* paragraph, RunRangeMap& runRanges, vint start, vint end)
			{
				AddStyleName(paragraph, runRanges, start, end, styleName);
			});
		}

		bool DocumentModel::RemoveStyleName(TextPos begin, TextPos end)
		{
			return EditContainer(begin, end, [=](DocumentParagraphRun* paragraph, RunRangeMap& runRanges, vint start, vint end)
			{
				document_editor::RemoveStyleName(paragraph, runRanges, start, end);
			});
		}

		bool DocumentModel::RenameStyle(const WString& oldStyleName, const WString& newStyleName)
		{
			vint index=styles.Keys().IndexOf(oldStyleName);
			if(index==-1) return false;
			if(styles.Keys().Contains(newStyleName)) return false;

			Ptr<DocumentStyle> style=styles.Values()[index];
			styles.Remove(oldStyleName);
			styles.Add(newStyleName, style);

			FOREACH(Ptr<DocumentStyle>, subStyle, styles.Values())
			{
				if(subStyle->parentStyleName==oldStyleName)
				{
					subStyle->parentStyleName=newStyleName;
				}
			}

			FOREACH(Ptr<DocumentParagraphRun>, paragraph, paragraphs)
			{
				ReplaceStyleName(paragraph.Obj(), oldStyleName, newStyleName);
			}
			return true;
		}

/***********************************************************************
DocumentModel::ClearStyle
***********************************************************************/

		bool DocumentModel::ClearStyle(TextPos begin, TextPos end)
		{
			return EditContainer(begin, end, [=](DocumentParagraphRun* paragraph, RunRangeMap& runRanges, vint start, vint end)
			{
				document_editor::ClearStyle(paragraph, runRanges, start, end);
			});
		}

/***********************************************************************
DocumentModel::ClearStyle
***********************************************************************/

		Ptr<DocumentStyleProperties> DocumentModel::SummarizeStyle(TextPos begin, TextPos end)
		{
			Ptr<DocumentStyleProperties> style;
			RunRangeMap runRanges;

			if (begin == end) goto END_OF_SUMMERIZING;

			// check caret range
			if (!CheckEditRange(begin, end, runRanges)) return nullptr;

			// Summarize container
			if (begin.row == end.row)
			{
				style = document_editor::SummarizeStyle(paragraphs[begin.row].Obj(), runRanges, this, begin.column, end.column);
			}
			else
			{
				for (vint i = begin.row; i <= end.row; i++)
				{
					Ptr<DocumentParagraphRun> paragraph = paragraphs[i];
					if (begin.row < i && i < end.row)
					{
						GetRunRange(paragraph.Obj(), runRanges);
					}
					RunRange range = runRanges[paragraph.Obj()];
					Ptr<DocumentStyleProperties> paragraphStyle;
					if (i == begin.row)
					{
						paragraphStyle = document_editor::SummarizeStyle(paragraph.Obj(), runRanges, this, begin.column, range.end);
					}
					else if (i == end.row)
					{
						paragraphStyle = document_editor::SummarizeStyle(paragraph.Obj(), runRanges, this, range.start, end.column);
					}
					else
					{
						paragraphStyle = document_editor::SummarizeStyle(paragraph.Obj(), runRanges, this, range.start, range.end);
					}

					if (!style)
					{
						style = paragraphStyle;
					}
					else if (paragraphStyle)
					{
						AggregateStyle(style, paragraphStyle);
					}
				}
			}

		END_OF_SUMMERIZING:
			if (!style)
			{
				style = new DocumentStyleProperties;
			}
			return style;
		}

		Nullable<WString> DocumentModel::SummarizeStyleName(TextPos begin, TextPos end)
		{
			if (begin == end) return {};

			// check caret range
			RunRangeMap runRanges;
			if (!CheckEditRange(begin, end, runRanges)) return nullptr;

			// Summarize container
			Nullable<WString> styleName;

			if (begin.row == end.row)
			{
				styleName = document_editor::SummarizeStyleName(paragraphs[begin.row].Obj(), runRanges, this, begin.column, end.column);
			}
			else
			{
				for (vint i = begin.row; i <= end.row; i++)
				{
					Ptr<DocumentParagraphRun> paragraph = paragraphs[i];
					if (begin.row < i && i < end.row)
					{
						GetRunRange(paragraph.Obj(), runRanges);
					}
					RunRange range = runRanges[paragraph.Obj()];
					Nullable<WString> newStyleName;
					if (i == begin.row)
					{
						newStyleName = document_editor::SummarizeStyleName(paragraph.Obj(), runRanges, this, begin.column, range.end);
					}
					else if (i == end.row)
					{
						newStyleName = document_editor::SummarizeStyleName(paragraph.Obj(), runRanges, this, range.start, end.column);
					}
					else
					{
						newStyleName = document_editor::SummarizeStyleName(paragraph.Obj(), runRanges, this, range.start, end.column);
					}

					if (i == begin.row)
					{
						styleName = newStyleName;
					}
					else if (!styleName || !newStyleName || styleName.Value() != newStyleName.Value())
					{
						styleName = {};
					}
				}
			}
			return styleName;
		}

		Nullable<Alignment> DocumentModel::SummarizeParagraphAlignment(TextPos begin, TextPos end)
		{
			bool left = false;
			bool center = false;
			bool right = false;

			RunRangeMap runRanges;
			if (!CheckEditRange(begin, end, runRanges)) return {};

			for (vint i = begin.row; i <= end.row; i++)
			{
				auto paragraph = paragraphs[i];
				if (paragraph->alignment)
				{
					switch (paragraph->alignment.Value())
					{
					case Alignment::Left:
						left = true;
						break;
					case Alignment::Center:
						center = true;
						break;
					case Alignment::Right:
						right = true;
						break;
					}
				}
				else
				{
					left = true;
				}
			}

			if (left && !center && !right) return Alignment::Left;
			if (!left && center && !right) return Alignment::Center;
			if (!left && !center && right) return Alignment::Right;
			return {};
		}
	}
}
