/** 
 * @file llview.cpp
 * @author James Cook
 * @brief Container for other views, anything that draws.
 *
 * $LicenseInfo:firstyear=2001&license=viewerlgpl$
 * Second Life Viewer Source Code
 * Copyright (C) 2010, Linden Research, Inc.
 * 
 * This library is free software; you can redistribute it and/or
 * modify it under the terms of the GNU Lesser General Public
 * License as published by the Free Software Foundation;
 * version 2.1 of the License only.
 * 
 * This library is distributed in the hope that it will be useful,
 * but WITHOUT ANY WARRANTY; without even the implied warranty of
 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
 * Lesser General Public License for more details.
 * 
 * You should have received a copy of the GNU Lesser General Public
 * License along with this library; if not, write to the Free Software
 * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA  02110-1301  USA
 * 
 * Linden Research, Inc., 945 Battery Street, San Francisco, CA  94111  USA
 * $/LicenseInfo$
 */

#include "linden_common.h"

#define LLVIEW_CPP
#include "llview.h"

#include <sstream>
#include <boost/tokenizer.hpp>

#include "llrender.h"
#include "llevent.h"
#include "llfocusmgr.h"
#include "llrect.h"
#include "llstl.h"
#include "llui.h"
#include "lluictrl.h"
#include "llwindow.h"
#include "v3color.h"
#include "lluictrlfactory.h"
#include "lltooltip.h"
#include "llsdutil.h"
#include "llsdserialize.h"
#include "llviewereventrecorder.h"
#include "llkeyboard.h"
// for ui edit hack
#include "llbutton.h"
#include "lllineeditor.h"
#include "lltexteditor.h"
#include "lltextbox.h"

static const S32 LINE_HEIGHT = 15;

S32		LLView::sDepth = 0;
bool	LLView::sDebugRects = false;
bool	LLView::sDebugRectsShowNames = true;
bool	LLView::sDebugKeys = false;
bool	LLView::sDebugMouseHandling = false;
std::string LLView::sMouseHandlerMessage;
BOOL	LLView::sForceReshape = FALSE;
std::set<LLView*> LLView::sPreviewHighlightedElements;
BOOL LLView::sHighlightingDiffs = FALSE;
LLView* LLView::sPreviewClickedElement = nullptr;
BOOL	LLView::sDrawPreviewHighlights = FALSE;
S32		LLView::sLastLeftXML = S32_MIN;
S32		LLView::sLastBottomXML = S32_MIN;
std::vector<LLViewDrawContext*> LLViewDrawContext::sDrawContextStack;

LLView::DrilldownFunc LLView::sDrilldown =
	boost::bind(&LLView::pointInView, _1, _2, _3, HIT_TEST_USE_BOUNDING_RECT);

//#if LL_DEBUG
BOOL LLView::sIsDrawing = FALSE;
//#endif

// Compiler optimization, generate extern template
template class LLView* LLView::getChild<class LLView>(
	const std::string& name, BOOL recurse) const;

static LLDefaultChildRegistry::Register<LLView> r("view");

namespace LLInitParam
{
	void TypeValues<LLView::EOrientation>::declareValues()
	{
		declare("horizontal", LLView::HORIZONTAL);
		declare("vertical", LLView::VERTICAL);	
	}
}


LLView::Follows::Follows()
:   string(""),
	flags("flags", FOLLOWS_LEFT | FOLLOWS_TOP)
{}

LLView::Params::Params()
:	name("name", std::string("unnamed")),
	enabled("enabled", true),
	visible("visible", true),
	mouse_opaque("mouse_opaque", true),
	use_bounding_rect("use_bounding_rect", false),
	from_xui("from_xui", false),
	focus_root("focus_root", false),
	tab_group("tab_group", 0),
	default_tab_group("default_tab_group"),
	tool_tip("tool_tip"),
	sound_flags("sound_flags", MOUSE_UP),
	follows("follows"),
	hover_cursor("hover_cursor", "UI_CURSOR_ARROW"),
	layout("layout"),
	rect("rect"),
	bottom_delta("bottom_delta", S32_MAX),
	top_pad("top_pad"),
	top_delta("top_delta", S32_MAX),
	left_pad("left_pad"),
	left_delta("left_delta", S32_MAX),
	needs_translate("translate"),
	xmlns("xmlns"),
	xmlns_xsi("xmlns:xsi"),
	xsi_schemaLocation("xsi:schemaLocation"),
	xsi_type("xsi:type")

{
	addSynonym(rect, "");
}

LLView::LLView(const LLView::Params& p)
:	LLTrace::MemTrackable<LLView>("LLView"),
	mHoverCursor(getCursorFromString(p.hover_cursor)),
	mParentView(nullptr),
	mVisible(p.visible),
	mName(p.name),
	mReshapeFlags(FOLLOWS_NONE),
	mDefaultTabGroup(p.default_tab_group),
	mLastTabGroup(0),
	mEnabled(p.enabled),
	mMouseOpaque(p.mouse_opaque),
	mSoundFlags(p.sound_flags),
	mFromXUI(p.from_xui),
	mIsFocusRoot(p.focus_root),
	mUseBoundingRect(p.use_bounding_rect),
	mLastVisible(FALSE),
	mInDraw(false),
	mDefaultWidgets(nullptr)
{
	// create rect first, as this will supply initial follows flags
	setShape(p.rect);
	parseFollowsFlags(p);
    
    //<alchemy>
    setToolTip(static_cast<LLStringExplicit>(p.tool_tip()));
}

LLView::~LLView()
{
	dirtyRect();
	//LL_INFOS() << "Deleting view " << mName << ":" << (void*) this << LL_ENDL;
	if (LLView::sIsDrawing)
	{
		LL_DEBUGS() << "Deleting view " << mName << " during UI draw() phase" << LL_ENDL;
	}
// 	llassert(LLView::sIsDrawing == FALSE);
	
//	llassert_always(sDepth == 0); // avoid deleting views while drawing! It can subtly break list iterators
	
	if( hasMouseCapture() )
	{
		//LL_WARNS() << "View holding mouse capture deleted: " << getName() << ".  Mouse capture removed." << LL_ENDL;
		gFocusMgr.removeMouseCaptureWithoutCallback( this );
	}

	deleteAllChildren();

	if (mParentView != nullptr)
	{
		mParentView->removeChild(this);
	}

	if (mDefaultWidgets)
	{
		delete mDefaultWidgets;
		mDefaultWidgets = nullptr;
	}
}

// virtual
BOOL LLView::isCtrl() const
{
	return FALSE;
}

// virtual
BOOL LLView::isPanel() const
{
	return FALSE;
}

void LLView::setToolTip(const LLStringExplicit& msg)
{
    // <alchemy>
    if (!msg.empty())
    {
        mToolTipMsg = std::make_unique<char[]>(msg.size() + 1);
        std::strncpy(mToolTipMsg.get(), msg.c_str(), msg.size() + 1);
    }
    else if (mToolTipMsg)
    {
        // C++17 defaults to nullptr, but before it defaults to creating a new pointer
        mToolTipMsg.reset(nullptr);
    }
    // </alchemy>
}

BOOL LLView::setToolTipArg(const LLStringExplicit& key, const LLStringExplicit& text)
{
    // <alchemy>
    if (!mTooltipArgs)
    {
        mTooltipArgs = std::make_unique<LLStringUtil::format_map_t>();
    }
    
    mTooltipArgs->emplace(key, text);
    // </alchemy>
	return TRUE;
}

void LLView::setToolTipArgs( const LLStringUtil::format_map_t& args )
{
	// <alchemy>
    if (!mTooltipArgs)
    {
        mTooltipArgs = std::make_unique<LLStringUtil::format_map_t>(args);
    }
    else
    {
        (*mTooltipArgs) = args;
    }
    // </alchemy>
}

// <alchemy>
const std::string LLView::getToolTip() const
{
    if (!mToolTipMsg || !mToolTipMsg[0])
        return std::string();
    
    if (mTooltipArgs)
    {
        LLUIString ui_str(mToolTipMsg.get(), *mTooltipArgs);
        return ui_str.getString();
    }
    else
    {
        LLUIString ui_str(mToolTipMsg.get());
        return ui_str.getString();
    }
}

// virtual
void LLView::setRect(const LLRect& rect)
{
	mRect = rect;
	updateBoundingRect();
}

void LLView::setUseBoundingRect( BOOL use_bounding_rect ) 
{
	if (mUseBoundingRect != use_bounding_rect)
	{
        mUseBoundingRect = use_bounding_rect; 
		updateBoundingRect();
	}
}

BOOL LLView::getUseBoundingRect() const
{
	return mUseBoundingRect;
}

// virtual
const std::string& LLView::getName() const
{
	static std::string no_name("(no name)");

	return mName.empty() ? no_name : mName;
}

void LLView::sendChildToFront(LLView* child)
{
// 	llassert_always(sDepth == 0); // Avoid re-ordering while drawing; it can cause subtle iterator bugs
	if (child && child->getParent() == this) 
	{
		// minor optimization, but more importantly,
		//  won't temporarily create an empty list
		if (child != mChildList.front())
		{
			mChildList.remove( child );
			mChildList.push_front(child);
		}
	}
}

void LLView::sendChildToBack(LLView* child)
{
// 	llassert_always(sDepth == 0); // Avoid re-ordering while drawing; it can cause subtle iterator bugs
	if (child && child->getParent() == this) 
	{
		// minor optimization, but more importantly,
		//  won't temporarily create an empty list
		if (child != mChildList.back())
		{
			mChildList.remove( child );
			mChildList.push_back(child);
		}
	}
}

// virtual
bool LLView::addChild(LLView* child, S32 tab_group)
{
	if (!child)
	{
		return false;
	}

	if (this == child) 
	{
		LL_ERRS() << "Adding view " << child->getName() << " as child of itself" << LL_ENDL;
	}

	// remove from current parent
	if (child->mParentView) 
	{
		child->mParentView->removeChild(child);
	}

	// add to front of child list, as normal
	mChildList.push_front(child);
	mChildHashMap.insert_or_assign(child->getName(), child); // <alchemy/>

	// add to tab order list
	if (tab_group != 0)
	{
		mTabOrder.insert(tab_order_pair_t(child, tab_group));
	}

	child->mParentView = this;
	updateBoundingRect();
	mLastTabGroup = tab_group;
	return true;
}


bool LLView::addChildInBack(LLView* child, S32 tab_group)
{
	if(addChild(child, tab_group))
	{
		sendChildToBack(child);
		return true;
	}

	return false;
}

// remove the specified child from the view, and set it's parent to NULL.
void LLView::removeChild(LLView* child)
{
	//llassert_always(sDepth == 0); // Avoid re-ordering while drawing; it can cause subtle iterator bugs
	if (child->mParentView == this) 
	{
		// if we are removing an item we are currently iterating over, that would be bad
		llassert(!child->mInDraw);
		mChildList.remove( child );

		for(auto it=mChildHashMap.begin(), end = mChildHashMap.end(); it != end; ++it)
		{
			if(it->second == child)
			{
				mChildHashMap.erase(it);
				break;
			}
		}

		child->mParentView = nullptr;
		child_tab_order_t::iterator found = mTabOrder.find(child);
		if(found != mTabOrder.end())
		{
			mTabOrder.erase(found);
		}
	}
	else
	{
		LL_WARNS() << "\"" << child->getName() << "\" is not a child of " << getName() << LL_ENDL;
	}
	updateBoundingRect();
}

BOOL LLView::isInVisibleChain() const
{
	BOOL visible = TRUE;

	const LLView* viewp = this;
	while(viewp)
	{
		if (!viewp->getVisible())
		{
			visible = FALSE;
			break;
		}
		viewp = viewp->getParent();
	}
	
	return visible;
}

BOOL LLView::isInEnabledChain() const
{
	BOOL enabled = TRUE;

	const LLView* viewp = this;
	while(viewp)
	{
		if (!viewp->getEnabled())
		{
			enabled = FALSE;
			break;
		}
		viewp = viewp->getParent();
	}
	
	return enabled;
}

static void buildPathname(std::ostream& out, const LLView* view)
{
	if (! (view && view->getParent()))
	{
		return; // Don't include root in the path.
	}

	buildPathname(out, view->getParent());

	// Build pathname into ostream on the way back from recursion.
	out << '/';

	// substitute all '/' in name with appropriate code
	std::string name = view->getName();
	std::size_t found = name.find('/');
	std::size_t start = 0;
	while (found != std::string::npos)
	{
		std::size_t sub_len = found - start;
		if (sub_len > 0)
		{
			out << name.substr(start, sub_len);
		}
		out << "%2F";
		start = found + 1;
		found = name.find('/', start);
	}
	if (start < name.size())
	{
		out << name.substr(start, name.size() - start);
	}
}

std::string LLView::getPathname() const
{
	std::ostringstream out;
	buildPathname(out, this);
	return out.str();
}

//static
std::string LLView::getPathname(const LLView* view)
{
    if (! view)
    {
        return "NULL";
    }
    return view->getPathname();
}

// virtual
BOOL LLView::canFocusChildren() const
{
	return TRUE;
}

//virtual
void LLView::setEnabled(BOOL enabled)
{
	mEnabled = enabled;
}

//virtual
bool LLView::isAvailable() const
{
    return isInEnabledChain() && isInVisibleChain();
}

//static
bool LLView::isAvailable(const LLView* view)
{
    return view && view->isAvailable();
}

//virtual
BOOL LLView::setLabelArg( const std::string& key, const LLStringExplicit& text )
{
	return FALSE;
}

//virtual
LLRect LLView::getSnapRect() const
{
	return mRect;
}

//virtual
LLRect LLView::getRequiredRect()
{
	return mRect;
}

BOOL LLView::focusNextRoot()
{
	LLView::child_list_t result = LLView::getFocusRootsQuery().run(this);
	return LLView::focusNext(result);
}

BOOL LLView::focusPrevRoot()
{
	LLView::child_list_t result = LLView::getFocusRootsQuery().run(this);
	return LLView::focusPrev(result);
}

// static
BOOL LLView::focusNext(LLView::child_list_t & result)
{
	LLView::child_list_reverse_iter_t focused = result.rend();
	for(LLView::child_list_reverse_iter_t iter = result.rbegin();
		iter != result.rend();
		++iter)
	{
		if(gFocusMgr.childHasKeyboardFocus(*iter))
		{
			focused = iter;
			break;
		}
	}
	LLView::child_list_reverse_iter_t next = focused;
	next = (next == result.rend()) ? result.rbegin() : ++next;
	while(next != focused)
	{
		// wrap around to beginning if necessary
		if(next == result.rend())
		{
			next = result.rbegin();
		}
		if((*next)->isCtrl())
		{
			LLUICtrl * ctrl = static_cast<LLUICtrl*>(*next);
			ctrl->setFocus(TRUE);
			ctrl->onTabInto();  
			gFocusMgr.triggerFocusFlash();
			return TRUE;
		}
		++next;
	}
	return FALSE;
}

// static
BOOL LLView::focusPrev(LLView::child_list_t & result)
{
	LLView::child_list_iter_t focused = result.end();
	for(LLView::child_list_iter_t iter = result.begin();
		iter != result.end();
		++iter)
	{
		if(gFocusMgr.childHasKeyboardFocus(*iter))
		{
			focused = iter;
			break;
		}
	}
	LLView::child_list_iter_t next = focused;
	next = (next == result.end()) ? result.begin() : ++next;
	while(next != focused)
	{
		// wrap around to beginning if necessary
		if(next == result.end())
		{
			next = result.begin();
		}
		if((*next)->isCtrl())
		{
			LLUICtrl * ctrl = static_cast<LLUICtrl*>(*next);
			if (!ctrl->hasFocus())
			{
				ctrl->setFocus(TRUE);
				ctrl->onTabInto();  
				gFocusMgr.triggerFocusFlash();
			}
			return TRUE;
		}
		++next;
	}
	return FALSE;
}

// delete all children. Override this function if you need to
// perform any extra clean up such as cached pointers to selected
// children, etc.
void LLView::deleteAllChildren()
{
	// clear out the control ordering
	mTabOrder.clear();

	while (!mChildList.empty())
	{
		LLView* viewp = mChildList.front();
		delete viewp; // will remove the child from mChildList
	}
	mChildHashMap.clear(); // <alchemy/>
}

void LLView::setAllChildrenEnabled(BOOL b)
{
	for (LLView* viewp : mChildList)
	{
		viewp->setEnabled(b);
	}
}

// virtual
void LLView::setVisible(BOOL visible)
{
	if ( mVisible != visible )
	{
		mVisible = visible;

		// notify children of visibility change if root, or part of visible hierarchy
		if (!getParent() || getParent()->isInVisibleChain())
		{
			// tell all children of this view that the visibility may have changed
			dirtyRect();
			onVisibilityChange( visible );
		}
		updateBoundingRect();
	}
}

// virtual
void LLView::onVisibilityChange ( BOOL new_visibility )
{
	BOOL old_visibility;
	bool log_visibility_change = LLViewerEventRecorder::instance().getLoggingStatus();
	for (LLView* viewp : mChildList)
	{
		if (!viewp)
		{
			continue;
		}

		// only views that are themselves visible will have their overall visibility affected by their ancestors
		old_visibility=viewp->getVisible();

		if(log_visibility_change)
		{
			if (old_visibility!=new_visibility)
			{
				LLViewerEventRecorder::instance().logVisibilityChange( viewp->getPathname(), viewp->getName(), new_visibility,"widget");
			}
		}

		if (old_visibility)
		{
			viewp->onVisibilityChange ( new_visibility );
		}

		if(log_visibility_change)
		{
			// Consider changing returns to confirm success and know which widget grabbed it
			// For now assume success and log at highest xui possible 
			// NOTE we log actual state - which may differ if it somehow failed to set visibility
			LL_DEBUGS() << "LLView::handleVisibilityChange	 - now: " << getVisible()  << " xui: " << viewp->getPathname() << " name: " << viewp->getName() << LL_ENDL;
		
		}
	}
}

// virtual
void LLView::translate(S32 x, S32 y)
{
	mRect.translate(x, y);
	updateBoundingRect();
}

// virtual
BOOL LLView::canSnapTo(const LLView* other_view)
{
	return other_view != this && other_view->getVisible();
}

// virtual
void LLView::setSnappedTo(const LLView* snap_view)
{
}

BOOL LLView::handleHover(S32 x, S32 y, MASK mask)
{
	return childrenHandleHover( x, y, mask ) != nullptr;
}

void LLView::onMouseEnter(S32 x, S32 y, MASK mask)
{
	//LL_INFOS() << "Mouse entered " << getName() << LL_ENDL;
}

void LLView::onMouseLeave(S32 x, S32 y, MASK mask)
{
	//LL_INFOS() << "Mouse left " << getName() << LL_ENDL;
}

bool LLView::visibleAndContains(S32 local_x, S32 local_y)
{
	return sDrilldown(this, local_x, local_y)
		&& getVisible();
}

bool LLView::visibleEnabledAndContains(S32 local_x, S32 local_y)
{
	return visibleAndContains(local_x, local_y)
		&& getEnabled();
}

// This is NOT event recording related
void LLView::logMouseEvent()
{
	if (sDebugMouseHandling)
	{
		sMouseHandlerMessage = std::string("/") + mName + sMouseHandlerMessage;
	}
}

template <typename METHOD, typename CHARTYPE>
LLView* LLView::childrenHandleCharEvent(const std::string& desc, const METHOD& method,
										CHARTYPE c, MASK mask)
{
	if ( getVisible() && getEnabled() )
	{
		for (LLView* viewp : mChildList)
		{
			if ((viewp->*method)(c, mask, TRUE))
			{
				if (LLView::sDebugKeys)
				{
					LL_INFOS() << desc << " handled by " << viewp->getName() << LL_ENDL;
				}
				return viewp;
			}
		}
	}
    return nullptr;
}

// XDATA might be MASK, or S32 clicks
template <typename METHOD, typename XDATA>
LLView* LLView::childrenHandleMouseEvent(const METHOD& method, S32 x, S32 y, XDATA extra, bool allow_mouse_block)
{
	for (LLView* viewp : mChildList)
	{
		S32 local_x = x - viewp->getRect().mLeft;
		S32 local_y = y - viewp->getRect().mBottom;

		if (!viewp->visibleEnabledAndContains(local_x, local_y))
		{
			continue;
		}

		if ((viewp->*method)( local_x, local_y, extra )
			|| (allow_mouse_block && viewp->blockMouseEvent( local_x, local_y )))
		{
			LL_DEBUGS() << "LLView::childrenHandleMouseEvent calling updatemouseeventinfo - local_x|global x  "<< local_x << " " << x	<< "local/global y " << local_y << " " << y << LL_ENDL;
			LL_DEBUGS() << "LLView::childrenHandleMouseEvent  getPathname for viewp result: " << viewp->getPathname() << "for this view: " << getPathname() << LL_ENDL;

			LLViewerEventRecorder::instance().updateMouseEventInfo(x,y,-55,-55,getPathname()); 

			// This is NOT event recording related
			viewp->logMouseEvent();

			return viewp;
		}
	}
	return nullptr;
}

LLView* LLView::childrenHandleToolTip(S32 x, S32 y, MASK mask)
{
	for (LLView* viewp : mChildList)
	{
		S32 local_x = x - viewp->getRect().mLeft;
		S32 local_y = y - viewp->getRect().mBottom;
		// Differs from childrenHandleMouseEvent() in that we want to offer
		// tooltips even for disabled widgets.
		if(!viewp->visibleAndContains(local_x, local_y))
		{
			continue;
		}

		if (viewp->handleToolTip(local_x, local_y, mask) 
			|| viewp->blockMouseEvent(local_x, local_y))
		{
			// This is NOT event recording related
			viewp->logMouseEvent();
			return viewp;
		}
	}
	return nullptr;
}

LLView* LLView::childrenHandleDragAndDrop(S32 x, S32 y, MASK mask,
									   BOOL drop,
									   EDragAndDropType cargo_type,
									   void* cargo_data,
									   EAcceptance* accept,
									   std::string& tooltip_msg)
{
	// default to not accepting drag and drop, will be overridden by handler
	*accept = ACCEPT_NO;

	for (LLView* viewp : mChildList)
	{
		S32 local_x = x - viewp->getRect().mLeft;
		S32 local_y = y - viewp->getRect().mBottom;
		if( !viewp->visibleEnabledAndContains(local_x, local_y))
		{
			continue;
		}

		// Differs from childrenHandleMouseEvent() simply in that this virtual
		// method call diverges pretty radically from the usual (x, y, int).
		if (viewp->handleDragAndDrop(local_x, local_y, mask, drop,
									 cargo_type,
									 cargo_data,
									 accept,
									 tooltip_msg)
			|| viewp->blockMouseEvent(local_x, local_y))
		{
			return viewp;
		}
	}
	return nullptr;
}

LLView* LLView::childrenHandleHover(S32 x, S32 y, MASK mask)
{
	for (LLView* viewp : mChildList)
	{
		S32 local_x = x - viewp->getRect().mLeft;
		S32 local_y = y - viewp->getRect().mBottom;
		if(!viewp->visibleEnabledAndContains(local_x, local_y))
		{
			continue;
		}

		// This call differentiates this method from childrenHandleMouseEvent().
		LLUI::getInstance()->mWindow->setCursor(viewp->getHoverCursor());

		if (viewp->handleHover(local_x, local_y, mask)
			|| viewp->blockMouseEvent(local_x, local_y))
		{
			// This is NOT event recording related
			viewp->logMouseEvent();
			return viewp;
		}
	}
	return nullptr;
}

LLView*	LLView::childFromPoint(S32 x, S32 y, bool recur)
{
	if (!getVisible())
		return nullptr;

	for (LLView* viewp : mChildList)
	{
		S32 local_x = x - viewp->getRect().mLeft;
		S32 local_y = y - viewp->getRect().mBottom;
		if (!viewp->visibleAndContains(local_x, local_y))
		{
			continue;
		}
		// Here we've found the first (frontmost) visible child at this level
		// containing the specified point. Is the caller asking us to drill
		// down and return the innermost leaf child at this point, or just the
		// top-level child?
		if (recur)
		{
			LLView* leaf(viewp->childFromPoint(local_x, local_y, recur));
			// Maybe viewp is already a leaf LLView, or maybe it has children
			// but this particular (x, y) point falls between them. If the
			// recursive call returns non-NULL, great, use that; else just use
			// viewp.
			return leaf? leaf : viewp;
		}
		return viewp;

	}
	return nullptr;
}

BOOL LLView::handleToolTip(S32 x, S32 y, MASK mask)
{
	BOOL handled = FALSE;

	// parents provide tooltips first, which are optionally
	// overridden by children, in case child is mouse_opaque
	std::string tooltip = getToolTip();
	if (!tooltip.empty())
	{
		// allow "scrubbing" over ui by showing next tooltip immediately
		// if previous one was still visible
		static LLUICachedControl<F32> tool_tip_fast_delay("ToolTipFastDelay", 0.1f);
		static LLUICachedControl<F32> tool_tip_delay("ToolTipDelay", 0.699999988079f);
		F32 timeout = LLToolTipMgr::instance().toolTipVisible() 
		              ? tool_tip_fast_delay
		              : tool_tip_delay;

		// Even if we don't show tooltips, consume the event, nothing below should show tooltip
		static LLUICachedControl<bool> allow_ui_tooltips("BasicUITooltips", true);
		if (allow_ui_tooltips)
		{
			LLToolTipMgr::instance().show(LLToolTip::Params()
			                              .message(tooltip)
			                              .sticky_rect(calcScreenRect())
			                              .delay_time(timeout));
		}
		handled = TRUE;
	}

	// child tooltips will override our own
	LLView* child_handler = childrenHandleToolTip(x, y, mask);
	if (child_handler)
	{
		handled = TRUE;
	}

	return handled;
}

BOOL LLView::handleKey(KEY key, MASK mask, BOOL called_from_parent)
{
	BOOL handled = FALSE;

	if (getVisible() && getEnabled())
	{
		if( called_from_parent )
		{
			// Downward traversal
			handled = childrenHandleKey( key, mask ) != nullptr;
		}

		if (!handled)
		{
			// For event logging we don't care which widget handles it
			// So we capture the key at the end of this function once we know if it was handled
			handled = handleKeyHere( key, mask );
			if (handled && LLView::sDebugKeys) // <alchemy/> - AHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHH
			{
				LL_DEBUGS() << "Key handled by " << getName() << LL_ENDL;
			}
		}
	}

	if( !handled && !called_from_parent && mParentView)
	{
		// Upward traversal
		handled = mParentView->handleKey( key, mask, FALSE );
	}
	return handled;
}

BOOL LLView::handleKeyUp(KEY key, MASK mask, BOOL called_from_parent)
{
	BOOL handled = FALSE;

	if (getVisible() && getEnabled())
	{
		if (called_from_parent)
		{
			// Downward traversal
			handled = childrenHandleKeyUp(key, mask) != nullptr;
		}

		if (!handled)
		{
			// For event logging we don't care which widget handles it
			// So we capture the key at the end of this function once we know if it was handled
			handled = handleKeyUpHere(key, mask);
			if (handled)
			{
				LL_DEBUGS() << "Key handled by " << getName() << LL_ENDL;
			}
		}
	}

	if (!handled && !called_from_parent && mParentView)
	{
		// Upward traversal
		handled = mParentView->handleKeyUp(key, mask, FALSE);
	}
	return handled;
}

// Called from handleKey()
// Handles key in this object.  Checking parents and children happens in handleKey()
BOOL LLView::handleKeyHere(KEY key, MASK mask)
{
	return FALSE;
}

// Called from handleKey()
// Handles key in this object.  Checking parents and children happens in handleKey()
BOOL LLView::handleKeyUpHere(KEY key, MASK mask)
{
	return FALSE;
}

BOOL LLView::handleUnicodeChar(llwchar uni_char, BOOL called_from_parent)
{
	BOOL handled = FALSE;

	if (getVisible() && getEnabled())
	{
		if( called_from_parent )
		{
			// Downward traversal
			handled = childrenHandleUnicodeChar( uni_char ) != nullptr;
		}

		if (!handled)
		{
			handled = handleUnicodeCharHere(uni_char);
			if (handled && LLView::sDebugKeys)
			{
				LL_INFOS() << "Unicode key handled by " << getName() << LL_ENDL;
			}
		}
	}

	if (!handled && !called_from_parent && mParentView)
	{
		// Upward traversal
		handled = mParentView->handleUnicodeChar(uni_char, FALSE);
	}

	if (handled)
	{
		LLViewerEventRecorder::instance().logKeyUnicodeEvent(uni_char);
	}
	
	return handled;
}


BOOL LLView::handleUnicodeCharHere(llwchar uni_char )
{
	return FALSE;
}


BOOL LLView::handleDragAndDrop(S32 x, S32 y, MASK mask, BOOL drop,
							   EDragAndDropType cargo_type, void* cargo_data,
							   EAcceptance* accept,
							   std::string& tooltip_msg)
{
	return childrenHandleDragAndDrop( x, y, mask, drop, cargo_type, cargo_data, accept, tooltip_msg) != nullptr;
}

void LLView::onMouseCaptureLost()
{
}

BOOL LLView::hasMouseCapture()
{ 
	return gFocusMgr.getMouseCapture() == this; 
}

BOOL LLView::handleMouseUp(S32 x, S32 y, MASK mask)
{
	LLView* r = childrenHandleMouseUp( x, y, mask );

	return (r!= nullptr);
}

BOOL LLView::handleMouseDown(S32 x, S32 y, MASK mask)
{
	LLView* r= childrenHandleMouseDown(x, y, mask );

	return (r!= nullptr);
}

BOOL LLView::handleDoubleClick(S32 x, S32 y, MASK mask)
{
	return childrenHandleDoubleClick( x, y, mask ) != nullptr;
}

BOOL LLView::handleScrollWheel(S32 x, S32 y, S32 clicks)
{
	return childrenHandleScrollWheel( x, y, clicks ) != nullptr;
}

BOOL LLView::handleScrollHWheel(S32 x, S32 y, S32 clicks)
{
	return childrenHandleScrollHWheel( x, y, clicks ) != NULL;
}

BOOL LLView::handleRightMouseDown(S32 x, S32 y, MASK mask)
{
	return childrenHandleRightMouseDown( x, y, mask ) != nullptr;
}

BOOL LLView::handleRightMouseUp(S32 x, S32 y, MASK mask)
{
	return childrenHandleRightMouseUp( x, y, mask ) != nullptr;
}
 
BOOL LLView::handleMiddleMouseDown(S32 x, S32 y, MASK mask)
{
	return childrenHandleMiddleMouseDown( x, y, mask ) != nullptr;
}

BOOL LLView::handleMiddleMouseUp(S32 x, S32 y, MASK mask)
{
	return childrenHandleMiddleMouseUp( x, y, mask ) != nullptr;
}

LLView* LLView::childrenHandleScrollWheel(S32 x, S32 y, S32 clicks)
{
	return childrenHandleMouseEvent(&LLView::handleScrollWheel, x, y, clicks, false);
}

LLView* LLView::childrenHandleScrollHWheel(S32 x, S32 y, S32 clicks)
{
	return childrenHandleMouseEvent(&LLView::handleScrollHWheel, x, y, clicks, false);
}

// Called during downward traversal
LLView* LLView::childrenHandleKey(KEY key, MASK mask)
{
	return childrenHandleCharEvent("Key", &LLView::handleKey, key, mask);
}

// Called during downward traversal
LLView* LLView::childrenHandleKeyUp(KEY key, MASK mask)
{
	return childrenHandleCharEvent("Key Up", &LLView::handleKeyUp, key, mask);
}

// Called during downward traversal
LLView* LLView::childrenHandleUnicodeChar(llwchar uni_char)
{
	return childrenHandleCharEvent("Unicode character", &LLView::handleUnicodeCharWithDummyMask,
								   uni_char, MASK_NONE);
}

LLView* LLView::childrenHandleMouseDown(S32 x, S32 y, MASK mask)
{
	return childrenHandleMouseEvent(&LLView::handleMouseDown, x, y, mask);
}

LLView* LLView::childrenHandleRightMouseDown(S32 x, S32 y, MASK mask)
{
	return childrenHandleMouseEvent(&LLView::handleRightMouseDown, x, y, mask);
}

LLView* LLView::childrenHandleMiddleMouseDown(S32 x, S32 y, MASK mask)
{
	return childrenHandleMouseEvent(&LLView::handleMiddleMouseDown, x, y, mask);
}

LLView* LLView::childrenHandleDoubleClick(S32 x, S32 y, MASK mask)
{
	return childrenHandleMouseEvent(&LLView::handleDoubleClick, x, y, mask);
}

LLView* LLView::childrenHandleMouseUp(S32 x, S32 y, MASK mask)
{
	return	childrenHandleMouseEvent(&LLView::handleMouseUp, x, y, mask);
}

LLView* LLView::childrenHandleRightMouseUp(S32 x, S32 y, MASK mask)
{
	return childrenHandleMouseEvent(&LLView::handleRightMouseUp, x, y, mask);
}

LLView* LLView::childrenHandleMiddleMouseUp(S32 x, S32 y, MASK mask)
{
	return childrenHandleMouseEvent(&LLView::handleMiddleMouseUp, x, y, mask);
}

void LLView::draw()
{
	drawChildren();
}

void LLView::drawChildren()
{
	if (!mChildList.empty())
	{
		LLView* rootp = LLUI::getInstance()->getRootView();		
		++sDepth;

		for (auto child_iter = mChildList.rbegin(), child_iter_end = mChildList.rend(); child_iter != child_iter_end;)  // ++child_iter)
		{
			child_list_reverse_iter_t child = child_iter++;
			LLView *viewp = *child;
			
			if (viewp == nullptr)
			{
				continue;
			}
			const LLRect& view_rect = viewp->getRect();
			if (viewp->getVisible() && view_rect.isValid())
			{
				LLRect screen_rect = viewp->calcScreenRect();
				if ( rootp->getLocalRect().overlaps(screen_rect)  && LLUI::getInstance()->mDirtyRect.overlaps(screen_rect))
				{
					LLUI::pushMatrix();
					{
						LLUI::translate((F32) view_rect.mLeft, (F32) view_rect.mBottom);
						// flag the fact we are in draw here, in case overridden draw() method attempts to remove this widget
						viewp->mInDraw = true;
						viewp->draw();
						viewp->mInDraw = false;

						if (sDebugRects)
						{
							viewp->drawDebugRect();

							// Check for bogus rectangle
							if (!getRect().isValid())
							{
								LL_WARNS() << "Bogus rectangle for " << getName() << " with " << mRect << LL_ENDL;
							}
						}
					}
					LLUI::popMatrix();
				}
			}

		}
		--sDepth;
	}
}

void LLView::dirtyRect()
{
	LLView* child = getParent();
	LLView* parent = child ? child->getParent() : NULL;
	LLView* cur = this;
	while (child && parent && parent->getParent())
	{ //find third to top-most view
		cur = child;
		child = parent;
		parent = parent->getParent();
	}

	LLUI::getInstance()->dirtyRect(cur->calcScreenRect());
}

//Draw a box for debugging.
void LLView::drawDebugRect()
{
	std::set<LLView*>::iterator preview_iter = std::find(sPreviewHighlightedElements.begin(), sPreviewHighlightedElements.end(), this);	// figure out if it's a previewed element

	LLUI::pushMatrix();
	{
		// drawing solids requires texturing be disabled
		gGL.getTexUnit(0)->unbind(LLTexUnit::TT_TEXTURE);

		if (getUseBoundingRect())
		{
			LLUI::translate((F32)mBoundingRect.mLeft - (F32)mRect.mLeft, (F32)mBoundingRect.mBottom - (F32)mRect.mBottom);
		}

		LLRect debug_rect = getUseBoundingRect() ? mBoundingRect : mRect;

		// draw red rectangle for the border
		LLColor4 border_color(0.25f, 0.25f, 0.25f, 1.f);
		if(preview_iter != sPreviewHighlightedElements.end())
		{
			if(LLView::sPreviewClickedElement && this == sPreviewClickedElement)
			{
				border_color = LLColor4::red;
			}
			else
			{
				static LLUIColor scroll_highlighted_color = LLUIColorTable::instance().getColor("ScrollHighlightedColor");
				border_color = scroll_highlighted_color;
			}
		}
		else
		{
			border_color.mV[sDepth%3] = 1.f;
		}

		gGL.color4fv( border_color.mV );

		gGL.begin(LLRender::LINES);
			gGL.vertex2i(0, debug_rect.getHeight() - 1);
			gGL.vertex2i(0, 0);

			gGL.vertex2i(0, 0);
			gGL.vertex2i(debug_rect.getWidth() - 1, 0);

			gGL.vertex2i(debug_rect.getWidth() - 1, 0);
			gGL.vertex2i(debug_rect.getWidth() - 1, debug_rect.getHeight() - 1);

			gGL.vertex2i(debug_rect.getWidth() - 1, debug_rect.getHeight() - 1);
			gGL.vertex2i(0, debug_rect.getHeight() - 1);
		gGL.end();

		// Draw the name if it's not a leaf node or not in editing or preview mode
		if (sDebugRectsShowNames 
			&& !mChildList.empty()
            && preview_iter == sPreviewHighlightedElements.end())
		{
			S32 x, y;
			gGL.color4fv( border_color.mV );

			x = debug_rect.getWidth() / 2;

			S32 rect_height = debug_rect.getHeight();
			S32 lines = rect_height / LINE_HEIGHT + 1;

			S32 depth = 0;
			LLView * viewp = this;
			while (nullptr != viewp)
			{
				viewp = viewp->getParent();
				depth++;
			}

			y = rect_height - LINE_HEIGHT * (depth % lines + 1);

			std::string debug_text = llformat("%s (%d x %d)", getName().c_str(),
										debug_rect.getWidth(), debug_rect.getHeight());
			LLFontGL::getFontSansSerifSmall()->renderUTF8(debug_text, 0, (F32)x, (F32)y, border_color,
												LLFontGL::HCENTER, LLFontGL::BASELINE, LLFontGL::NORMAL, LLFontGL::NO_SHADOW,
												S32_MAX, S32_MAX, nullptr, FALSE);
		}
	}
	LLUI::popMatrix();
}

void LLView::drawChild(LLView* childp, S32 x_offset, S32 y_offset, BOOL force_draw)
{
	if (childp && childp->getParent() == this)
	{
		++sDepth;

		if ((childp->getVisible() && childp->getRect().isValid()) 
			|| force_draw)
		{
			gGL.matrixMode(LLRender::MM_MODELVIEW);
			LLUI::pushMatrix();
			{
				LLUI::translate((F32)childp->getRect().mLeft + x_offset, (F32)childp->getRect().mBottom + y_offset);
				childp->draw();
			}
			LLUI::popMatrix();
		}

		--sDepth;
	}
}


void LLView::reshape(S32 width, S32 height, BOOL called_from_parent)
{
	// compute how much things changed and apply reshape logic to children
	S32 delta_width = width - getRect().getWidth();
	S32 delta_height = height - getRect().getHeight();

	if (delta_width || delta_height || sForceReshape)
	{
		// adjust our rectangle
		mRect.mRight = getRect().mLeft + width;
		mRect.mTop = getRect().mBottom + height;

		// move child views according to reshape flags
		for (LLView* viewp : mChildList)
		{
			if (viewp != nullptr)
			{
			LLRect child_rect( viewp->mRect );

			if (viewp->followsRight() && viewp->followsLeft())
			{
				child_rect.mRight += delta_width;
			}
			else if (viewp->followsRight())
			{
				child_rect.mLeft += delta_width;
				child_rect.mRight += delta_width;
			}
			else if (viewp->followsLeft())
			{
				// left is 0, don't need to adjust coords
			}
			else
			{
				// BUG what to do when we don't follow anyone?
				// for now, same as followsLeft
			}

			if (viewp->followsTop() && viewp->followsBottom())
			{
				child_rect.mTop += delta_height;
			}
			else if (viewp->followsTop())
			{
				child_rect.mTop += delta_height;
				child_rect.mBottom += delta_height;
			}
			else if (viewp->followsBottom())
			{
				// bottom is 0, so don't need to adjust coords
			}
			else
			{
				// BUG what to do when we don't follow?
				// for now, same as bottom
			}

			S32 delta_x = child_rect.mLeft - viewp->getRect().mLeft;
			S32 delta_y = child_rect.mBottom - viewp->getRect().mBottom;
			viewp->translate( delta_x, delta_y );
			if (child_rect.getWidth() != viewp->getRect().getWidth() || child_rect.getHeight() != viewp->getRect().getHeight())
			{
				viewp->reshape(child_rect.getWidth(), child_rect.getHeight());
			}
		}
	}
	}

	if (!called_from_parent)
	{
		if (mParentView)
		{
			mParentView->reshape(mParentView->getRect().getWidth(), mParentView->getRect().getHeight(), FALSE);
		}
	}

	updateBoundingRect();
}

LLRect LLView::calcBoundingRect()
{
	LLRect local_bounding_rect = LLRect::null;

	for (LLView* childp : mChildList)
	{
		// ignore invisible and "top" children when calculating bounding rect
		// such as combobox popups
		if (!childp->getVisible() || childp == gFocusMgr.getTopCtrl()) 
		{
			continue;
		}

		LLRect child_bounding_rect = childp->getBoundingRect();

		if (local_bounding_rect.isEmpty())
		{
			// start out with bounding rect equal to first visible child's bounding rect
			local_bounding_rect = child_bounding_rect;
		}
		else
		{
			// accumulate non-null children rectangles
			if (!child_bounding_rect.isEmpty())
			{
				local_bounding_rect.unionWith(child_bounding_rect);
			}
		}
	}

	// convert to parent-relative coordinates
	local_bounding_rect.translate(mRect.mLeft, mRect.mBottom);
	return local_bounding_rect;
}


void LLView::updateBoundingRect()
{
	if (isDead()) return;

	LLRect cur_rect = mBoundingRect;

	if (getUseBoundingRect())
	{
		mBoundingRect = calcBoundingRect();
	}
	else
	{
		mBoundingRect = mRect;
	}

	// give parent view a chance to resize, in case we just moved, for example
	if (getParent() && getParent()->getUseBoundingRect())
	{
		getParent()->updateBoundingRect();
	}

	if (mBoundingRect != cur_rect)
	{
		dirtyRect();
	}

}

LLRect LLView::calcScreenRect() const
{
	LLRect screen_rect;
	localPointToScreen(0, 0, &screen_rect.mLeft, &screen_rect.mBottom);
	localPointToScreen(getRect().getWidth(), getRect().getHeight(), &screen_rect.mRight, &screen_rect.mTop);
	return screen_rect;
}

LLRect LLView::calcScreenBoundingRect() const
{
	LLRect screen_rect;
	// get bounding rect, if used
	LLRect bounding_rect = getUseBoundingRect() ? mBoundingRect : mRect;

	// convert to local coordinates, as defined by mRect
	bounding_rect.translate(-mRect.mLeft, -mRect.mBottom);

	localPointToScreen(bounding_rect.mLeft, bounding_rect.mBottom, &screen_rect.mLeft, &screen_rect.mBottom);
	localPointToScreen(bounding_rect.mRight, bounding_rect.mTop, &screen_rect.mRight, &screen_rect.mTop);
	return screen_rect;
}

LLRect LLView::getLocalBoundingRect() const
{
	LLRect local_bounding_rect = getBoundingRect();
	local_bounding_rect.translate(-mRect.mLeft, -mRect.mBottom);

	return local_bounding_rect;
}


LLRect LLView::getLocalRect() const
{
	const auto& rect = getRect();
	return LLRect(0, rect.getHeight(), rect.getWidth(), 0);
}

LLRect LLView::getLocalSnapRect() const
{
	LLRect local_snap_rect = getSnapRect();
	local_snap_rect.translate(-getRect().mLeft, -getRect().mBottom);
	return local_snap_rect;
}

BOOL LLView::hasAncestor(const LLView* parentp) const
{
	if (!parentp)
	{
		return FALSE;
	}

	LLView* viewp = getParent();
	while(viewp)
	{
		if (viewp == parentp)
		{
			return TRUE;
		}
		viewp = viewp->getParent();
	}

	return FALSE;
}

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

BOOL LLView::childHasKeyboardFocus( const std::string& childname ) const
{
	LLView *focus = dynamic_cast<LLView *>(gFocusMgr.getKeyboardFocus());
	
	while (focus != nullptr)
	{
		if (focus->getName() == childname)
		{
			return TRUE;
		}
		
		focus = focus->getParent();
	}
	
	return FALSE;
}

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

BOOL LLView::hasChild(const std::string& childname, BOOL recurse) const
{
	return findChildView(childname, recurse) != nullptr;
}

//-----------------------------------------------------------------------------
// getChildView()
//-----------------------------------------------------------------------------
LLView* LLView::getChildView(const std::string& name, BOOL recurse) const
{
	return getChild<LLView>(name, recurse);
}

static LLTrace::BlockTimerStatHandle FTM_FIND_VIEWS("Find Widgets");

// <alchemy> - Hashmap
LLView* LLView::findChildView(const std::string& name, BOOL recurse) const
{
	LL_RECORD_BLOCK_TIME(FTM_FIND_VIEWS);
	//richard: should we allow empty names?
	//if(name.empty())
	//	return NULL;
	// Look for direct children *first*
	/*for (LLView* childp : mChildList)
	{
		llassert(childp);
		if (childp->getName() == name)
		{
			return childp;
		}
	}*/
	const auto it = mChildHashMap.find(name);
	if(it != mChildHashMap.cend())
	{
		return it->second;
	}
	if (recurse)
	{
		// Look inside each child as well.
		for (LLView* childp : mChildList)
		{
			llassert(childp);
			LLView* viewp = childp->findChildView(name, recurse);
			if ( viewp )
			{
				return viewp;
			}
		}
	}
	return nullptr;
}
// </alchemy>

BOOL LLView::parentPointInView(S32 x, S32 y, EHitTestType type) const 
{ 
	return (getUseBoundingRect() && type == HIT_TEST_USE_BOUNDING_RECT)
		? mBoundingRect.pointInRect( x, y ) 
		: mRect.pointInRect( x, y ); 
}

BOOL LLView::pointInView(S32 x, S32 y, EHitTestType type) const 
{ 
	return (getUseBoundingRect() && type == HIT_TEST_USE_BOUNDING_RECT)
		? mBoundingRect.pointInRect( x + mRect.mLeft, y + mRect.mBottom ) 
		: mRect.localPointInRect( x, y ); 
}

BOOL LLView::blockMouseEvent(S32 x, S32 y) const
{
	return mMouseOpaque && pointInView(x, y, HIT_TEST_IGNORE_BOUNDING_RECT);
}

// virtual
void LLView::screenPointToLocal(S32 screen_x, S32 screen_y, S32* local_x, S32* local_y) const
{
	*local_x = screen_x - getRect().mLeft;
	*local_y = screen_y - getRect().mBottom;

	const LLView* cur = this;
	while( cur->mParentView )
	{
		cur = cur->mParentView;
		*local_x -= cur->getRect().mLeft;
		*local_y -= cur->getRect().mBottom;
	}
}

void LLView::localPointToScreen(S32 local_x, S32 local_y, S32* screen_x, S32* screen_y) const
{
	*screen_x = local_x;
	*screen_y = local_y;

	const LLView* cur = this;
	do
	{
		const LLRect& cur_rect = cur->getRect();
		*screen_x += cur_rect.mLeft;
		*screen_y += cur_rect.mBottom;
		cur = cur->mParentView;
	}
	while( cur );
}

void LLView::screenRectToLocal(const LLRect& screen, LLRect* local) const
{
	*local = screen;
	local->translate( -getRect().mLeft, -getRect().mBottom );

	const LLView* cur = this;
	while( cur->mParentView )
	{
		cur = cur->mParentView;
		local->translate( -cur->getRect().mLeft, -cur->getRect().mBottom );
	}
}

void LLView::localRectToScreen(const LLRect& local, LLRect* screen) const
{
	*screen = local;
	screen->translate( getRect().mLeft, getRect().mBottom );

	const LLView* cur = this;
	while( cur->mParentView )
	{
		cur = cur->mParentView;
		screen->translate( cur->getRect().mLeft, cur->getRect().mBottom );
	}
}

LLView* LLView::getRootView()
{
	LLView* view = this;
	while( view->mParentView )
	{
		view = view->mParentView;
	}
	return view;
}

LLView* LLView::findPrevSibling(LLView* child)
{
	child_list_t::iterator prev_it = std::find(mChildList.begin(), mChildList.end(), child);
	if (prev_it != mChildList.end() && prev_it != mChildList.begin())
	{
		return *(--prev_it);
	}
	return nullptr;
}

LLView* LLView::findNextSibling(LLView* child)
{
	child_list_t::iterator next_it = std::find(mChildList.begin(), mChildList.end(), child);
	if (next_it != mChildList.end())
	{
		next_it++;
	}

	return (next_it != mChildList.end()) ? *next_it : NULL;
}


LLCoordGL getNeededTranslation(const LLRect& input, const LLRect& constraint, S32 min_overlap_pixels)
{
	LLCoordGL delta;

	const S32 KEEP_ONSCREEN_PIXELS_WIDTH = llmin(min_overlap_pixels, input.getWidth());
	const S32 KEEP_ONSCREEN_PIXELS_HEIGHT = llmin(min_overlap_pixels, input.getHeight());

	if( input.mRight - KEEP_ONSCREEN_PIXELS_WIDTH < constraint.mLeft )
	{
		delta.mX = constraint.mLeft - (input.mRight - KEEP_ONSCREEN_PIXELS_WIDTH);
	}
	else if( input.mLeft + KEEP_ONSCREEN_PIXELS_WIDTH > constraint.mRight )
	{
		delta.mX = constraint.mRight - (input.mLeft + KEEP_ONSCREEN_PIXELS_WIDTH);
	}

	if( input.mTop > constraint.mTop )
	{
		delta.mY = constraint.mTop - input.mTop;
	}
	else
	if( input.mTop - KEEP_ONSCREEN_PIXELS_HEIGHT < constraint.mBottom )
	{
		delta.mY = constraint.mBottom - (input.mTop - KEEP_ONSCREEN_PIXELS_HEIGHT);
	}

	return delta;
}

// Moves the view so that it is entirely inside of constraint.
// If the view will not fit because it's too big, aligns with the top and left.
// (Why top and left?  That's where the drag bars are for floaters.)
BOOL LLView::translateIntoRect(const LLRect& constraint, S32 min_overlap_pixels)
{
	LLCoordGL translation = getNeededTranslation(getRect(), constraint, min_overlap_pixels);

	if (translation.mX != 0 || translation.mY != 0)
	{
		translate(translation.mX, translation.mY);
		return TRUE;
	}
	return FALSE;
}

// move this view into "inside" but not onto "exclude"
// NOTE: if this view is already contained in "inside", we ignore the "exclude" rect
BOOL LLView::translateIntoRectWithExclusion( const LLRect& inside, const LLRect& exclude, S32 min_overlap_pixels)
{
	LLCoordGL translation = getNeededTranslation(getRect(), inside, min_overlap_pixels);
	
	if (translation.mX != 0 || translation.mY != 0)
	{
		// translate ourselves into constraint rect
		translate(translation.mX, translation.mY);
	
		// do we overlap with exclusion area?
		// keep moving in the same direction to the other side of the exclusion rect
		if (exclude.overlaps(getRect()))
		{
			// moving right
			if (translation.mX > 0)
			{
				translate(exclude.mRight - getRect().mLeft, 0);
			}
			// moving left
			else if (translation.mX < 0)
			{
				translate(exclude.mLeft - getRect().mRight, 0);
			}

			// moving up
			if (translation.mY > 0)
			{
				translate(0, exclude.mTop - getRect().mBottom);
			}
			// moving down
			else if (translation.mY < 0)
			{
				translate(0, exclude.mBottom - getRect().mTop);
			}
		}

		return TRUE;
	}
	return FALSE;
}


void LLView::centerWithin(const LLRect& bounds)
{
	S32 left   = bounds.mLeft + (bounds.getWidth() - getRect().getWidth()) / 2;
	S32 bottom = bounds.mBottom + (bounds.getHeight() - getRect().getHeight()) / 2;

	translate( left - getRect().mLeft, bottom - getRect().mBottom );
}

BOOL LLView::localPointToOtherView( S32 x, S32 y, S32 *other_x, S32 *other_y, const LLView* other_view) const
{
	const LLView* cur_view = this;
	const LLView* root_view = nullptr;

	while (cur_view)
	{
		if (cur_view == other_view)
		{
			*other_x = x;
			*other_y = y;
			return TRUE;
		}

		x += cur_view->getRect().mLeft;
		y += cur_view->getRect().mBottom;

		cur_view = cur_view->getParent();
		root_view = cur_view;
	}

	// assuming common root between two views, chase other_view's parents up to root
	cur_view = other_view;
	while (cur_view)
	{
		x -= cur_view->getRect().mLeft;
		y -= cur_view->getRect().mBottom;

		cur_view = cur_view->getParent();

		if (cur_view == root_view)
		{
			*other_x = x;
			*other_y = y;
			return TRUE;
		}
	}

	*other_x = x;
	*other_y = y;
	return FALSE;
}

BOOL LLView::localRectToOtherView( const LLRect& local, LLRect* other, const LLView* other_view ) const
{
	LLRect cur_rect = local;
	const LLView* cur_view = this;
	const LLView* root_view = nullptr;

	while (cur_view)
	{
		if (cur_view == other_view)
		{
			*other = cur_rect;
			return TRUE;
		}

		cur_rect.translate(cur_view->getRect().mLeft, cur_view->getRect().mBottom);

		cur_view = cur_view->getParent();
		root_view = cur_view;
	}

	// assuming common root between two views, chase other_view's parents up to root
	cur_view = other_view;
	while (cur_view)
	{
		cur_rect.translate(-cur_view->getRect().mLeft, -cur_view->getRect().mBottom);

		cur_view = cur_view->getParent();

		if (cur_view == root_view)
		{
			*other = cur_rect;
			return TRUE;
		}
	}

	*other = cur_rect;
	return FALSE;
}


class CompareByTabOrder
{
public:
	CompareByTabOrder(const LLView::child_tab_order_t& order, S32 default_tab_group = 0) 
		:	mTabOrder(order),
		mDefaultTabGroup(default_tab_group)
	{}
	virtual ~CompareByTabOrder() = default;

	// This method compares two LLViews by the tab order specified in the comparator object.  The
	// code for this is a little convoluted because each argument can have four states:
	// 1) not a control, 2) a control but not in the tab order, 3) a control in the tab order, 4) null
	bool operator() (const LLView* const a, const LLView* const b) const
	{
		S32 a_group = 0, b_group = 0;
		if(!a) return false;
		if(!b) return true;

		LLView::child_tab_order_const_iter_t a_found = mTabOrder.find(a), b_found = mTabOrder.find(b);
		if(a_found != mTabOrder.end())
		{
			a_group = a_found->second;
		}
		if(b_found != mTabOrder.end())
		{
			b_group = b_found->second;
		}

		if(a_group < mDefaultTabGroup && b_group >= mDefaultTabGroup) return true;
		if(b_group < mDefaultTabGroup && a_group >= mDefaultTabGroup) return false;
		return a_group > b_group;  // sort correctly if they're both on the same side of the default tab groupreturn a > b;
	}
private:
	// ok to store a reference, as this should only be allocated on stack during view query operations
	const LLView::child_tab_order_t& mTabOrder;
	const S32 mDefaultTabGroup;
};

class SortByTabOrder final : public LLQuerySorter, public LLSingleton<SortByTabOrder>
{
	LLSINGLETON_EMPTY_CTOR(SortByTabOrder);
	/*virtual*/ void sort(LLView * parent, LLView::child_list_t &children) const override
	{
		children.sort(CompareByTabOrder(parent->getTabOrder(), parent->getDefaultTabGroup()));
	}
};

// static
const LLViewQuery & LLView::getTabOrderQuery()
{
	static LLViewQuery query;
	if(query.getPreFilters().empty()) {
		query.addPreFilter(LLVisibleFilter::getInstance());
		query.addPreFilter(LLEnabledFilter::getInstance());
		query.addPreFilter(LLTabStopFilter::getInstance());
		query.addPostFilter(LLLeavesFilter::getInstance());
		query.setSorter(SortByTabOrder::getInstance());
	}
	return query;
}

// This class is only used internally by getFocusRootsQuery below. 
class LLFocusRootsFilter final : public LLQueryFilter, public LLSingleton<LLFocusRootsFilter>
{
	LLSINGLETON_EMPTY_CTOR(LLFocusRootsFilter);
	/*virtual*/ filterResult_t operator() (const LLView* const view, const viewList_t & children) const override
	{
		return filterResult_t(view->isCtrl() && view->isFocusRoot(), !view->isFocusRoot());
	}
};

// static
const LLViewQuery & LLView::getFocusRootsQuery()
{
	static LLViewQuery query;
	if(query.getPreFilters().empty()) {
		query.addPreFilter(LLVisibleFilter::getInstance());
		query.addPreFilter(LLEnabledFilter::getInstance());
		query.addPreFilter(LLFocusRootsFilter::getInstance());
		query.addPostFilter(LLRootsFilter::getInstance());
	}
	return query;
}


void	LLView::setShape(const LLRect& new_rect, bool by_user)
{
	if (new_rect != getRect())
	{
		handleReshape(new_rect, by_user);
	}
}

void LLView::handleReshape(const LLRect& new_rect, bool by_user)
{
	reshape(new_rect.getWidth(), new_rect.getHeight());
	translate(new_rect.mLeft - getRect().mLeft, new_rect.mBottom - getRect().mBottom);
}

LLView* LLView::findSnapRect(LLRect& new_rect, const LLCoordGL& mouse_dir,
							 LLView::ESnapType snap_type, S32 threshold, S32 padding)
{
	new_rect = mRect;
	LLView* snap_view = nullptr;

	if (!mParentView)
	{
		return nullptr;
	}
	
	S32 delta_x = 0;
	S32 delta_y = 0;
	if (mouse_dir.mX >= 0)
	{
		S32 new_right = mRect.mRight;
		LLView* view = findSnapEdge(new_right, mouse_dir, SNAP_RIGHT, snap_type, threshold, padding);
		delta_x = new_right - mRect.mRight;
		snap_view = view ? view : snap_view;
	}

	if (mouse_dir.mX <= 0)
	{
		S32 new_left = mRect.mLeft;
		LLView* view = findSnapEdge(new_left, mouse_dir, SNAP_LEFT, snap_type, threshold, padding);
		delta_x = new_left - mRect.mLeft;
		snap_view = view ? view : snap_view;
	}

	if (mouse_dir.mY >= 0)
	{
		S32 new_top = mRect.mTop;
		LLView* view = findSnapEdge(new_top, mouse_dir, SNAP_TOP, snap_type, threshold, padding);
		delta_y = new_top - mRect.mTop;
		snap_view = view ? view : snap_view;
	}

	if (mouse_dir.mY <= 0)
	{
		S32 new_bottom = mRect.mBottom;
		LLView* view = findSnapEdge(new_bottom, mouse_dir, SNAP_BOTTOM, snap_type, threshold, padding);
		delta_y = new_bottom - mRect.mBottom;
		snap_view = view ? view : snap_view;
	}

	new_rect.translate(delta_x, delta_y);
	return snap_view;
}

LLView*	LLView::findSnapEdge(S32& new_edge_val, const LLCoordGL& mouse_dir, ESnapEdge snap_edge, ESnapType snap_type, S32 threshold, S32 padding)
{
	LLRect snap_rect = getSnapRect();
	S32 snap_pos = 0;
	switch(snap_edge)
	{
	case SNAP_LEFT:
		snap_pos = snap_rect.mLeft;
		break;
	case SNAP_RIGHT:
		snap_pos = snap_rect.mRight;
		break;
	case SNAP_TOP:
		snap_pos = snap_rect.mTop;
		break;
	case SNAP_BOTTOM:
		snap_pos = snap_rect.mBottom;
		break;
	}

	if (!mParentView)
	{
		new_edge_val = snap_pos;
		return nullptr;
	}

	LLView* snap_view = nullptr;

	// If the view is near the edge of its parent, snap it to
	// the edge.
	LLRect test_rect = snap_rect;
	test_rect.stretch(padding);

	S32 x_threshold = threshold;
	S32 y_threshold = threshold;

	LLRect parent_local_snap_rect = mParentView->getLocalSnapRect();

	if (snap_type == SNAP_PARENT || snap_type == SNAP_PARENT_AND_SIBLINGS)
	{
		switch(snap_edge)
		{
		case SNAP_RIGHT:
			if (llabs(parent_local_snap_rect.mRight - test_rect.mRight) <= x_threshold 
				&& (parent_local_snap_rect.mRight - test_rect.mRight) * mouse_dir.mX >= 0)
			{
				snap_pos = parent_local_snap_rect.mRight - padding;
				snap_view = mParentView;
				x_threshold = llabs(parent_local_snap_rect.mRight - test_rect.mRight);
			}
			break;
		case SNAP_LEFT:
			if (llabs(test_rect.mLeft - parent_local_snap_rect.mLeft) <= x_threshold 
				&& test_rect.mLeft * mouse_dir.mX <= 0)
			{
				snap_pos = parent_local_snap_rect.mLeft + padding;
				snap_view = mParentView;
				x_threshold = llabs(test_rect.mLeft - parent_local_snap_rect.mLeft);
			}
			break;
		case SNAP_BOTTOM:
			if (llabs(test_rect.mBottom - parent_local_snap_rect.mBottom) <= y_threshold 
				&& test_rect.mBottom * mouse_dir.mY <= 0)
			{
				snap_pos = parent_local_snap_rect.mBottom + padding;
				snap_view = mParentView;
				y_threshold = llabs(test_rect.mBottom - parent_local_snap_rect.mBottom);
			}
			break;
		case SNAP_TOP:
			if (llabs(parent_local_snap_rect.mTop - test_rect.mTop) <= y_threshold && (parent_local_snap_rect.mTop - test_rect.mTop) * mouse_dir.mY >= 0)
			{
				snap_pos = parent_local_snap_rect.mTop - padding;
				snap_view = mParentView;
				y_threshold = llabs(parent_local_snap_rect.mTop - test_rect.mTop);
			}
			break;
		default:
			LL_ERRS() << "Invalid snap edge" << LL_ENDL;
		}
	}

	if (snap_type == SNAP_SIBLINGS || snap_type == SNAP_PARENT_AND_SIBLINGS)
	{
		for (auto siblingp : *mParentView->getChildList())
        {
            if (!canSnapTo(siblingp)) continue;

			LLRect sibling_rect = siblingp->getSnapRect();

			switch(snap_edge)
			{
			case SNAP_RIGHT:
				if (llabs(test_rect.mRight - sibling_rect.mLeft) <= x_threshold 
					&& (test_rect.mRight - sibling_rect.mLeft) * mouse_dir.mX <= 0)
				{
					snap_pos = sibling_rect.mLeft - padding;
					snap_view = siblingp;
					x_threshold = llabs(test_rect.mRight - sibling_rect.mLeft);
				}
				// if snapped with sibling along other axis, check for shared edge
				else if (llabs(sibling_rect.mTop - (test_rect.mBottom - padding)) <= y_threshold 
					|| llabs(sibling_rect.mBottom - (test_rect.mTop + padding)) <= x_threshold)
				{
					if (llabs(test_rect.mRight - sibling_rect.mRight) <= x_threshold 
						&& (test_rect.mRight - sibling_rect.mRight) * mouse_dir.mX <= 0)
					{
						snap_pos = sibling_rect.mRight;
						snap_view = siblingp;
						x_threshold = llabs(test_rect.mRight - sibling_rect.mRight);
					}
				}
				break;
			case SNAP_LEFT:
				if (llabs(test_rect.mLeft - sibling_rect.mRight) <= x_threshold 
					&& (test_rect.mLeft - sibling_rect.mRight) * mouse_dir.mX <= 0)
				{
					snap_pos = sibling_rect.mRight + padding;
					snap_view = siblingp;
					x_threshold = llabs(test_rect.mLeft - sibling_rect.mRight);				
				}
				// if snapped with sibling along other axis, check for shared edge
				else if (llabs(sibling_rect.mTop - (test_rect.mBottom - padding)) <= y_threshold 
					|| llabs(sibling_rect.mBottom - (test_rect.mTop + padding)) <= y_threshold)
				{
					if (llabs(test_rect.mLeft - sibling_rect.mLeft) <= x_threshold 
						&& (test_rect.mLeft - sibling_rect.mLeft) * mouse_dir.mX <= 0)
					{
						snap_pos = sibling_rect.mLeft;
						snap_view = siblingp;
						x_threshold = llabs(test_rect.mLeft - sibling_rect.mLeft);									
					}
				}
				break;
			case SNAP_BOTTOM:
				if (llabs(test_rect.mBottom - sibling_rect.mTop) <= y_threshold 
					&& (test_rect.mBottom - sibling_rect.mTop) * mouse_dir.mY <= 0)
				{
					snap_pos = sibling_rect.mTop + padding;
					snap_view = siblingp;
					y_threshold = llabs(test_rect.mBottom - sibling_rect.mTop);
				}
				// if snapped with sibling along other axis, check for shared edge
				else if (llabs(sibling_rect.mRight - (test_rect.mLeft - padding)) <= x_threshold 
					|| llabs(sibling_rect.mLeft - (test_rect.mRight + padding)) <= x_threshold)
				{
					if (llabs(test_rect.mBottom - sibling_rect.mBottom) <= y_threshold 
						&& (test_rect.mBottom - sibling_rect.mBottom) * mouse_dir.mY <= 0)
					{
						snap_pos = sibling_rect.mBottom;
						snap_view = siblingp;
						y_threshold = llabs(test_rect.mBottom - sibling_rect.mBottom);
					}
				}
				break;
			case SNAP_TOP:
				if (llabs(test_rect.mTop - sibling_rect.mBottom) <= y_threshold 
					&& (test_rect.mTop - sibling_rect.mBottom) * mouse_dir.mY <= 0)
				{
					snap_pos = sibling_rect.mBottom - padding;
					snap_view = siblingp;
					y_threshold = llabs(test_rect.mTop - sibling_rect.mBottom);
				}
				// if snapped with sibling along other axis, check for shared edge
				else if (llabs(sibling_rect.mRight - (test_rect.mLeft - padding)) <= x_threshold 
					|| llabs(sibling_rect.mLeft - (test_rect.mRight + padding)) <= x_threshold)
				{
					if (llabs(test_rect.mTop - sibling_rect.mTop) <= y_threshold 
						&& (test_rect.mTop - sibling_rect.mTop) * mouse_dir.mY <= 0)
					{
						snap_pos = sibling_rect.mTop;
						snap_view = siblingp;
						y_threshold = llabs(test_rect.mTop - sibling_rect.mTop);
					}
				}
				break;
			default:
				LL_ERRS() << "Invalid snap edge" << LL_ENDL;
			}
		}
	}

	new_edge_val = snap_pos;
	return snap_view;
}

//-----------------------------------------------------------------------------
// Listener dispatch functions
//-----------------------------------------------------------------------------


LLControlVariable *LLView::findControl(const std::string& name)
{
	// parse the name to locate which group it belongs to
	std::size_t key_pos = name.find('.');
	if(key_pos != std::string::npos)
	{
		std::string control_group_key = name.substr(0, key_pos);
		LLControlVariable* control;
		// check if it's in the control group that name indicated
		if(LLUI::getInstance()->mSettingGroups[control_group_key])
		{
			control = LLUI::getInstance()->mSettingGroups[control_group_key]->getControl(name);
			if (control)
			{
				return control;
			}
		}
	}
	
	LLControlGroup& control_group = LLUI::getInstance()->getControlControlGroup(name);
	return control_group.getControl(name);	
}

void LLView::initFromParams(const LLView::Params& params)
{
	LLRect required_rect = getRequiredRect();

	S32 width = llmax(getRect().getWidth(), required_rect.getWidth());
	S32 height = llmax(getRect().getHeight(), required_rect.getHeight());

	reshape(width, height);

	// call virtual methods with most recent data
	// use getters because these values might not come through parameter block
	setEnabled(getEnabled());
	setVisible(getVisible());

	if (!params.name().empty())
	{
		setName(params.name());
	}

	mLayout = params.layout();
}

void LLView::parseFollowsFlags(const LLView::Params& params)
{
	// preserve follows flags set by code if user did not override
	if (!params.follows.isProvided()) 
	{
		return;
	}

	// interpret either string or bitfield version of follows
	if (params.follows.string.isChosen())
	{
		setFollows(FOLLOWS_NONE);

		std::string follows = params.follows.string;

		typedef boost::tokenizer<boost::char_separator<char> > tokenizer;
		boost::char_separator<char> sep("|");
		tokenizer tokens(follows, sep);
		tokenizer::iterator token_iter = tokens.begin();

		while(token_iter != tokens.end())
		{
			const std::string& token_str = *token_iter;
			if (token_str == "left")
			{
				mReshapeFlags |= FOLLOWS_LEFT;
			}
			else if (token_str == "right")
			{
				mReshapeFlags |= FOLLOWS_RIGHT;
			}
			else if (token_str == "top")
			{
				mReshapeFlags |= FOLLOWS_TOP;
			}
			else if (token_str == "bottom")
			{
				mReshapeFlags |= FOLLOWS_BOTTOM;
			}
			else if (token_str == "all")
			{
				mReshapeFlags |= FOLLOWS_ALL;
			}
			++token_iter;
		}
	}
	else if (params.follows.flags.isChosen())
	{
		setFollows(params.follows.flags);
	}
}


// static
//LLFontGL::HAlign LLView::selectFontHAlign(LLXMLNodePtr node)
//{
//	LLFontGL::HAlign gl_hfont_align = LLFontGL::LEFT;
//
//	if (node->hasAttribute("halign"))
//	{
//		std::string horizontal_align_name;
//		node->getAttributeString("halign", horizontal_align_name);
//		gl_hfont_align = LLFontGL::hAlignFromName(horizontal_align_name);
//	}
//	return gl_hfont_align;
//}

// Return the rectangle of the last-constructed child,
// if present and a first-class widget (eg, not a close box or drag handle)
// Returns true if found
static bool get_last_child_rect(LLView* parent, LLRect *rect)
{
	if (!parent) return false;

	LLView::child_list_t::const_iterator itor = 
		parent->getChildList()->begin();
	for (;itor != parent->getChildList()->end(); ++itor)
	{
		LLView *last_view = (*itor);
		if (last_view->getFromXUI())
		{
			*rect = last_view->getRect();
			return true;
		}
	}
	return false;
}

//static
void LLView::applyXUILayout(LLView::Params& p, LLView* parent, LLRect layout_rect)
{
	if (!parent) return;

	const S32 VPAD = 4;
	const S32 MIN_WIDGET_HEIGHT = 10;
	
	// *NOTE:  This will confuse export of floater/panel coordinates unless
	// the default is also "topleft".  JC
	if (p.layout().empty())
	{
		p.layout = parent->getLayout();
	}

	if (layout_rect.isEmpty())
	{
		layout_rect = parent->getLocalRect();
	}

	// overwrite uninitialized rect params, using context
	LLRect default_rect = parent->getLocalRect();

	bool layout_topleft = (p.layout() == "topleft");

	// convert negative or centered coordinates to parent relative values
	// Note: some of this logic matches the logic in TypedParam<LLRect>::setValueFromBlock()
	if (p.rect.left.isProvided()) 
	{
		p.rect.left = p.rect.left + ((p.rect.left >= 0) ? layout_rect.mLeft : layout_rect.mRight);
	}
	if (p.rect.right.isProvided())
	{
		p.rect.right = p.rect.right + ((p.rect.right >= 0) ? layout_rect.mLeft : layout_rect.mRight);
	}
	if (p.rect.bottom.isProvided()) 
	{
		p.rect.bottom = p.rect.bottom + ((p.rect.bottom >= 0) ? layout_rect.mBottom : layout_rect.mTop);
		if (layout_topleft)
		{
			//invert top to bottom
			p.rect.bottom = layout_rect.mBottom + layout_rect.mTop - p.rect.bottom;
		}
	}
	if (p.rect.top.isProvided())
	{
		p.rect.top = p.rect.top + ((p.rect.top >= 0) ? layout_rect.mBottom : layout_rect.mTop);
		if (layout_topleft)
		{
			//invert top to bottom
			p.rect.top = layout_rect.mBottom + layout_rect.mTop - p.rect.top;
		}
	}

	// DEPRECATE: automatically fall back to height of MIN_WIDGET_HEIGHT pixels
	if (!p.rect.height.isProvided() && !p.rect.top.isProvided() && p.rect.height == 0)
	{
		p.rect.height = MIN_WIDGET_HEIGHT;
	}

	default_rect.translate(0, default_rect.getHeight());

	// If there was a recently constructed child, use its rectangle
	get_last_child_rect(parent, &default_rect);

	if (layout_topleft)
	{
		// Invert the sense of bottom_delta for topleft layout
		if (p.bottom_delta.isProvided())
		{
			p.bottom_delta = -p.bottom_delta;
		}
		else if (p.top_pad.isProvided()) 
		{
			p.bottom_delta = -(p.rect.height + p.top_pad);
		}
		else if (p.top_delta.isProvided())
		{
			p.bottom_delta =
				-(p.top_delta + p.rect.height - default_rect.getHeight());
		}
		else if (!p.left_delta.isProvided()
					&& !p.left_pad.isProvided())
		{
			// set default position is just below last rect
			p.bottom_delta.set(-(p.rect.height + VPAD), false);
		}
		else
		{
			p.bottom_delta.set(0, false);
		}
	
		// default to same left edge
		if (!p.left_delta.isProvided())
		{
			p.left_delta.set(0, false);
		}
		if (p.left_pad.isProvided())
		{
			// left_pad is based on prior widget's right edge
			p.left_delta.set(p.left_pad + default_rect.getWidth(), false);
		}
			
		default_rect.translate(p.left_delta, p.bottom_delta);				
	}
	else
	{	
		// set default position is just below last rect
		if (!p.bottom_delta.isProvided())
		{
			p.bottom_delta.set(-(p.rect.height + VPAD), false);
		}
		if (!p.left_delta.isProvided())
		{
			p.left_delta.set(0, false);
		}
		default_rect.translate(p.left_delta, p.bottom_delta);
	}

	// this handles case where *both* x and x_delta are provided
	// ignore x in favor of default x + x_delta
	if (p.bottom_delta.isProvided()) p.rect.bottom.set(0, false);
	if (p.left_delta.isProvided()) p.rect.left.set(0, false);

	// selectively apply rectangle defaults, making sure that
	// params are not flagged as having been "provided"
	// as rect params are overconstrained and rely on provided flags
	if (!p.rect.left.isProvided())
	{
		p.rect.left.set(default_rect.mLeft, false);
		//HACK: get around the fact that setting a rect param component value won't invalidate the existing rect object value
		p.rect.paramChanged(p.rect.left, true);
	}
	if (!p.rect.bottom.isProvided())
	{
		p.rect.bottom.set(default_rect.mBottom, false);
		p.rect.paramChanged(p.rect.bottom, true);
	}
	if (!p.rect.top.isProvided())
	{
		p.rect.top.set(default_rect.mTop, false);
		p.rect.paramChanged(p.rect.top, true);
	}
	if (!p.rect.right.isProvided())
	{
		p.rect.right.set(default_rect.mRight, false);
		p.rect.paramChanged(p.rect.right, true);

	}
	if (!p.rect.width.isProvided())
	{
		p.rect.width.set(default_rect.getWidth(), false);
		p.rect.paramChanged(p.rect.width, true);
	}
	if (!p.rect.height.isProvided())
	{
		p.rect.height.set(default_rect.getHeight(), false);
		p.rect.paramChanged(p.rect.height, true);
	}
}

static S32 invert_vertical(S32 y, LLView* parent)
{
	if (y < 0)
	{
		// already based on top-left, just invert
		return -y;
	}
	else if (parent)
	{
		// use parent to flip coordinate
		S32 parent_height = parent->getRect().getHeight();
		return parent_height - y;
	}
	else
	{
		LL_WARNS() << "Attempting to convert layout to top-left with no parent" << LL_ENDL;
		return y;
	}
}

// Assumes that input is in bottom-left coordinates, hence must call
// _before_ convert_coords_to_top_left().
static void convert_to_relative_layout(LLView::Params& p, LLView* parent)
{
	// Use setupParams to get the final widget rectangle
	// according to our wacky layout rules.
	LLView::Params final = p;
	LLView::applyXUILayout(final, parent);
	// Must actually extract the rectangle to get consistent
	// right = left+width, top = bottom+height
	LLRect final_rect = final.rect;

	// We prefer to write out top edge instead of bottom, regardless
	// of whether we use relative positioning
	bool converted_top = false;

	// Look for a last rectangle
	LLRect last_rect;
	if (get_last_child_rect(parent, &last_rect))
	{
		// ...we have a previous widget to compare to
		const S32 EDGE_THRESHOLD_PIXELS = 4;
		S32 left_pad = final_rect.mLeft - last_rect.mRight;
		S32 left_delta = final_rect.mLeft - last_rect.mLeft;
		S32 top_pad = final_rect.mTop - last_rect.mBottom;
		S32 top_delta = final_rect.mTop - last_rect.mTop;
		// If my left edge is almost the same, or my top edge is
		// almost the same...
		if (llabs(left_delta) <= EDGE_THRESHOLD_PIXELS
			|| llabs(top_delta) <= EDGE_THRESHOLD_PIXELS)
		{
			// ...use relative positioning
			// prefer top_pad if widgets are stacking vertically
			// (coordinate system is still bottom-left here)
			if (top_pad < 0)
			{
				p.top_pad = top_pad;
				p.top_delta.setProvided(false);
			}
			else
			{
				p.top_pad.setProvided(false);
				p.top_delta = top_delta;
			}
			// null out other vertical specifiers
			p.rect.top.setProvided(false);
			p.rect.bottom.setProvided(false);
			p.bottom_delta.setProvided(false);
			converted_top = true;

			// prefer left_pad if widgets are stacking horizontally
			if (left_pad > 0)
			{
				p.left_pad = left_pad;
				p.left_delta.setProvided(false);
			}
			else
			{
				p.left_pad.setProvided(false);
				p.left_delta = left_delta;
			}
			p.rect.left.setProvided(false);
			p.rect.right.setProvided(false);
		}
	}

	if (!converted_top)
	{
		// ...this is the first widget, or one that wasn't aligned
		// prefer top/left specification
		p.rect.top = final_rect.mTop;
		p.rect.bottom.setProvided(false);
		p.bottom_delta.setProvided(false);
		p.top_pad.setProvided(false);
		p.top_delta.setProvided(false);
	}
}

static void convert_coords_to_top_left(LLView::Params& p, LLView* parent)
{
	// Convert the coordinate system to be top-left based.
	if (p.rect.top.isProvided())
	{
		p.rect.top = invert_vertical(p.rect.top, parent);
	}
	if (p.rect.bottom.isProvided())
	{
		p.rect.bottom = invert_vertical(p.rect.bottom, parent);
	}
	if (p.top_pad.isProvided())
	{
		p.top_pad = -p.top_pad;
	}
	if (p.top_delta.isProvided())
	{
		p.top_delta = -p.top_delta;
	}
	if (p.bottom_delta.isProvided())
	{
		p.bottom_delta = -p.bottom_delta;
	}
	p.layout = "topleft";
}

//static
void LLView::setupParamsForExport(Params& p, LLView* parent)
{
	// Don't convert if already top-left based
	if (p.layout() == "topleft") 
	{
		return;
	}

	// heuristic:  Many of our floaters and panels were bulk-exported.
	// These specify exactly bottom/left and height/width.
	// Others were done by hand using bottom_delta and/or left_delta.
	// Some rely on not specifying left to mean align with left edge.
	// Try to convert both to use relative layout, but using top-left
	// coordinates.
	// Avoid rectangles where top/bottom/left/right was specified.
	if (p.rect.height.isProvided() && p.rect.width.isProvided())
	{
		if (p.rect.bottom.isProvided() && p.rect.left.isProvided())
		{
			// standard bulk export, convert it
			convert_to_relative_layout(p, parent);
		}
		else if (p.rect.bottom.isProvided() && p.left_delta.isProvided())
		{
			// hand layout with left_delta
			convert_to_relative_layout(p, parent);
		}
		else if (p.bottom_delta.isProvided())
		{
			// hand layout with bottom_delta
			// don't check for p.rect.left or p.left_delta because sometimes
			// this layout doesn't set it for widgets that are left-aligned
			convert_to_relative_layout(p, parent);
		}
	}

	convert_coords_to_top_left(p, parent);
}

LLView::tree_iterator_t LLView::beginTreeDFS() 
{ 
	return tree_iterator_t(this, 
							boost::bind(boost::mem_fn(&LLView::beginChild), _1), 
							boost::bind(boost::mem_fn(&LLView::endChild), _1)); 
}

LLView::tree_iterator_t LLView::endTreeDFS() 
{ 
	// an empty iterator is an "end" iterator
	return tree_iterator_t();
}

LLView::tree_post_iterator_t LLView::beginTreeDFSPost() 
{ 
	return tree_post_iterator_t(this, 
							boost::bind(boost::mem_fn(&LLView::beginChild), _1), 
							boost::bind(boost::mem_fn(&LLView::endChild), _1)); 
}

LLView::tree_post_iterator_t LLView::endTreeDFSPost() 
{ 
	// an empty iterator is an "end" iterator
	return tree_post_iterator_t();
}

LLView::bfs_tree_iterator_t LLView::beginTreeBFS() 
{ 
	return bfs_tree_iterator_t(this, 
							boost::bind(boost::mem_fn(&LLView::beginChild), _1), 
							boost::bind(boost::mem_fn(&LLView::endChild), _1)); 
}

LLView::bfs_tree_iterator_t LLView::endTreeBFS() 
{ 
	// an empty iterator is an "end" iterator
	return bfs_tree_iterator_t();
}


LLView::root_to_view_iterator_t LLView::beginRootToView()
{
	return root_to_view_iterator_t(this, boost::bind(&LLView::getParent, _1));
}

LLView::root_to_view_iterator_t LLView::endRootToView()
{
	return root_to_view_iterator_t();
}


// only create maps on demand, as they incur heap allocation/deallocation cost
// when a view is constructed/deconstructed
LLView& LLView::getDefaultWidgetContainer() const
{
	if (!mDefaultWidgets)
	{
		LLView::Params p;
		p.name = "default widget container";
		p.visible = false; // ensures default widgets can't steal focus, etc.
		mDefaultWidgets = new LLView(p);
	}
	return *mDefaultWidgets;
}

S32	LLView::notifyParent(const LLSD& info)
{
	LLView* parent = getParent();
	if(parent)
		return parent->notifyParent(info);
	return 0;
}
bool	LLView::notifyChildren(const LLSD& info)
{
	bool ret = false;
	for (LLView* childp : mChildList)
	{
		ret = ret || childp->notifyChildren(info);
	}
	return ret;
}

// convenient accessor for draw context
const LLViewDrawContext& LLView::getDrawContext()
{
	return LLViewDrawContext::getCurrentContext();
}

const LLViewDrawContext& LLViewDrawContext::getCurrentContext()
{
	static LLViewDrawContext default_context;

	if (sDrawContextStack.empty())
		return default_context;
		
	return *sDrawContextStack.back();
}

LLSD LLView::getInfo(void)
{
	LLSD info;
	addInfo(info);
	return info;
}

void LLView::addInfo(LLSD & info)
{
	info["path"] = getPathname();
	info["class"] = typeid(*this).name();
	info["visible"] = getVisible();
	info["visible_chain"] = isInVisibleChain();
	info["enabled"] = getEnabled();
	info["enabled_chain"] = isInEnabledChain();
	info["available"] = isAvailable();
	LLRect rect(calcScreenRect());
	info["rect"] = LLSDMap("left", rect.mLeft)("top", rect.mTop)
				("right", rect.mRight)("bottom", rect.mBottom);
}
