PageRenderTime 155ms CodeModel.GetById 22ms app.highlight 119ms RepoModel.GetById 1ms app.codeStats 0ms

/indra/llui/llview.cpp

https://bitbucket.org/lindenlab/viewer-beta/
C++ | 2605 lines | 2018 code | 332 blank | 255 comment | 409 complexity | d169bde8dce686124b74748ac9fde121 MD5 | raw file

Large files files are truncated, but you can click here to view the full file

   1/** 
   2 * @file llview.cpp
   3 * @author James Cook
   4 * @brief Container for other views, anything that draws.
   5 *
   6 * $LicenseInfo:firstyear=2001&license=viewerlgpl$
   7 * Second Life Viewer Source Code
   8 * Copyright (C) 2010, Linden Research, Inc.
   9 * 
  10 * This library is free software; you can redistribute it and/or
  11 * modify it under the terms of the GNU Lesser General Public
  12 * License as published by the Free Software Foundation;
  13 * version 2.1 of the License only.
  14 * 
  15 * This library is distributed in the hope that it will be useful,
  16 * but WITHOUT ANY WARRANTY; without even the implied warranty of
  17 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
  18 * Lesser General Public License for more details.
  19 * 
  20 * You should have received a copy of the GNU Lesser General Public
  21 * License along with this library; if not, write to the Free Software
  22 * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA  02110-1301  USA
  23 * 
  24 * Linden Research, Inc., 945 Battery Street, San Francisco, CA  94111  USA
  25 * $/LicenseInfo$
  26 */
  27
  28#include "linden_common.h"
  29
  30#define LLVIEW_CPP
  31#include "llview.h"
  32
  33#include <cassert>
  34#include <sstream>
  35#include <boost/tokenizer.hpp>
  36#include <boost/foreach.hpp>
  37#include <boost/bind.hpp>
  38
  39#include "llrender.h"
  40#include "llevent.h"
  41#include "llfocusmgr.h"
  42#include "llrect.h"
  43#include "llstl.h"
  44#include "llui.h"
  45#include "lluictrl.h"
  46#include "llwindow.h"
  47#include "v3color.h"
  48#include "lluictrlfactory.h"
  49#include "lltooltip.h"
  50#include "llsdutil.h"
  51
  52// for ui edit hack
  53#include "llbutton.h"
  54#include "lllineeditor.h"
  55#include "lltexteditor.h"
  56#include "lltextbox.h"
  57
  58S32		LLView::sDepth = 0;
  59bool	LLView::sDebugRects = false;
  60bool	LLView::sDebugRectsShowNames = true;
  61bool	LLView::sDebugKeys = false;
  62bool	LLView::sDebugMouseHandling = false;
  63std::string LLView::sMouseHandlerMessage;
  64BOOL	LLView::sForceReshape = FALSE;
  65std::set<LLView*> LLView::sPreviewHighlightedElements;
  66BOOL LLView::sHighlightingDiffs = FALSE;
  67LLView* LLView::sPreviewClickedElement = NULL;
  68BOOL	LLView::sDrawPreviewHighlights = FALSE;
  69S32		LLView::sLastLeftXML = S32_MIN;
  70S32		LLView::sLastBottomXML = S32_MIN;
  71std::vector<LLViewDrawContext*> LLViewDrawContext::sDrawContextStack;
  72
  73LLView::DrilldownFunc LLView::sDrilldown =
  74	boost::bind(&LLView::pointInView, _1, _2, _3, HIT_TEST_USE_BOUNDING_RECT);
  75
  76//#if LL_DEBUG
  77BOOL LLView::sIsDrawing = FALSE;
  78//#endif
  79
  80// Compiler optimization, generate extern template
  81template class LLView* LLView::getChild<class LLView>(
  82	const std::string& name, BOOL recurse) const;
  83
  84static LLDefaultChildRegistry::Register<LLView> r("view");
  85
  86LLView::Follows::Follows()
  87:   string(""),
  88	flags("flags", FOLLOWS_LEFT | FOLLOWS_TOP)
  89{}
  90
  91LLView::Params::Params()
  92:	name("name", std::string("unnamed")),
  93	enabled("enabled", true),
  94	visible("visible", true),
  95	mouse_opaque("mouse_opaque", true),
  96	follows("follows"),
  97	hover_cursor("hover_cursor", "UI_CURSOR_ARROW"),
  98	use_bounding_rect("use_bounding_rect", false),
  99	tab_group("tab_group", 0),
 100	default_tab_group("default_tab_group"),
 101	tool_tip("tool_tip"),
 102	sound_flags("sound_flags", MOUSE_UP),
 103	layout("layout"),
 104	rect("rect"),
 105	bottom_delta("bottom_delta", S32_MAX),
 106	top_pad("top_pad"),
 107	top_delta("top_delta", S32_MAX),
 108	left_pad("left_pad"),
 109	left_delta("left_delta", S32_MAX),
 110	from_xui("from_xui", false),
 111	focus_root("focus_root", false),
 112	needs_translate("translate"),
 113	xmlns("xmlns"),
 114	xmlns_xsi("xmlns:xsi"),
 115	xsi_schemaLocation("xsi:schemaLocation"),
 116	xsi_type("xsi:type")
 117
 118{
 119	addSynonym(rect, "");
 120}
 121
 122LLView::LLView(const LLView::Params& p)
 123:	mVisible(p.visible),
 124	mInDraw(false),
 125	mName(p.name),
 126	mParentView(NULL),
 127	mReshapeFlags(FOLLOWS_NONE),
 128	mFromXUI(p.from_xui),
 129	mIsFocusRoot(p.focus_root),
 130	mLastVisible(FALSE),
 131	mNextInsertionOrdinal(0),
 132	mHoverCursor(getCursorFromString(p.hover_cursor)),
 133	mEnabled(p.enabled),
 134	mMouseOpaque(p.mouse_opaque),
 135	mSoundFlags(p.sound_flags),
 136	mUseBoundingRect(p.use_bounding_rect),
 137	mDefaultTabGroup(p.default_tab_group),
 138	mLastTabGroup(0),
 139	mToolTipMsg((LLStringExplicit)p.tool_tip()),
 140	mDefaultWidgets(NULL)
 141{
 142	// create rect first, as this will supply initial follows flags
 143	setShape(p.rect);
 144	parseFollowsFlags(p);
 145}
 146
 147LLView::~LLView()
 148{
 149	dirtyRect();
 150	//llinfos << "Deleting view " << mName << ":" << (void*) this << llendl;
 151	if (LLView::sIsDrawing)
 152	{
 153		lldebugs << "Deleting view " << mName << " during UI draw() phase" << llendl;
 154	}
 155// 	llassert(LLView::sIsDrawing == FALSE);
 156	
 157//	llassert_always(sDepth == 0); // avoid deleting views while drawing! It can subtly break list iterators
 158	
 159	if( hasMouseCapture() )
 160	{
 161		//llwarns << "View holding mouse capture deleted: " << getName() << ".  Mouse capture removed." << llendl;
 162		gFocusMgr.removeMouseCaptureWithoutCallback( this );
 163	}
 164
 165	deleteAllChildren();
 166
 167	if (mParentView != NULL)
 168	{
 169		mParentView->removeChild(this);
 170	}
 171
 172	if (mDefaultWidgets)
 173	{
 174		delete mDefaultWidgets;
 175		mDefaultWidgets = NULL;
 176	}
 177}
 178
 179// virtual
 180BOOL LLView::isCtrl() const
 181{
 182	return FALSE;
 183}
 184
 185// virtual
 186BOOL LLView::isPanel() const
 187{
 188	return FALSE;
 189}
 190
 191void LLView::setToolTip(const LLStringExplicit& msg)
 192{
 193	mToolTipMsg = msg;
 194}
 195
 196BOOL LLView::setToolTipArg(const LLStringExplicit& key, const LLStringExplicit& text)
 197{
 198	mToolTipMsg.setArg(key, text);
 199	return TRUE;
 200}
 201
 202void LLView::setToolTipArgs( const LLStringUtil::format_map_t& args )
 203{
 204	mToolTipMsg.setArgList(args);
 205}
 206
 207// virtual
 208void LLView::setRect(const LLRect& rect)
 209{
 210	mRect = rect;
 211	updateBoundingRect();
 212}
 213
 214void LLView::setUseBoundingRect( BOOL use_bounding_rect ) 
 215{
 216	if (mUseBoundingRect != use_bounding_rect)
 217	{
 218        mUseBoundingRect = use_bounding_rect; 
 219		updateBoundingRect();
 220	}
 221}
 222
 223BOOL LLView::getUseBoundingRect() const
 224{
 225	return mUseBoundingRect;
 226}
 227
 228// virtual
 229const std::string& LLView::getName() const
 230{
 231	static std::string no_name("(no name)");
 232
 233	return mName.empty() ? no_name : mName;
 234}
 235
 236void LLView::sendChildToFront(LLView* child)
 237{
 238// 	llassert_always(sDepth == 0); // Avoid re-ordering while drawing; it can cause subtle iterator bugs
 239	if (child && child->getParent() == this) 
 240	{
 241		// minor optimization, but more importantly,
 242		//  won't temporarily create an empty list
 243		if (child != mChildList.front())
 244		{
 245			mChildList.remove( child );
 246			mChildList.push_front(child);
 247		}
 248	}
 249}
 250
 251void LLView::sendChildToBack(LLView* child)
 252{
 253// 	llassert_always(sDepth == 0); // Avoid re-ordering while drawing; it can cause subtle iterator bugs
 254	if (child && child->getParent() == this) 
 255	{
 256		// minor optimization, but more importantly,
 257		//  won't temporarily create an empty list
 258		if (child != mChildList.back())
 259		{
 260			mChildList.remove( child );
 261			mChildList.push_back(child);
 262		}
 263	}
 264}
 265
 266void LLView::moveChildToFrontOfTabGroup(LLUICtrl* child)
 267{
 268	if(mCtrlOrder.find(child) != mCtrlOrder.end())
 269	{
 270		mCtrlOrder[child].second = -1 * mNextInsertionOrdinal++;
 271	}
 272}
 273
 274void LLView::moveChildToBackOfTabGroup(LLUICtrl* child)
 275{
 276	if(mCtrlOrder.find(child) != mCtrlOrder.end())
 277	{
 278		mCtrlOrder[child].second = mNextInsertionOrdinal++;
 279	}
 280}
 281
 282// virtual
 283bool LLView::addChild(LLView* child, S32 tab_group)
 284{
 285	if (!child)
 286	{
 287		return false;
 288	}
 289	if (mParentView == child) 
 290	{
 291		llerrs << "Adding view " << child->getName() << " as child of itself" << llendl;
 292	}
 293
 294	// remove from current parent
 295	if (child->mParentView) 
 296	{
 297		child->mParentView->removeChild(child);
 298	}
 299
 300	// add to front of child list, as normal
 301	mChildList.push_front(child);
 302
 303	// add to ctrl list if is LLUICtrl
 304	if (child->isCtrl())
 305	{
 306		LLUICtrl* ctrl = static_cast<LLUICtrl*>(child);
 307		mCtrlOrder.insert(tab_order_pair_t(ctrl,
 308							tab_order_t(tab_group, mNextInsertionOrdinal)));
 309
 310		mNextInsertionOrdinal++;
 311	}
 312
 313	child->mParentView = this;
 314	updateBoundingRect();
 315	mLastTabGroup = tab_group;
 316	return true;
 317}
 318
 319
 320bool LLView::addChildInBack(LLView* child, S32 tab_group)
 321{
 322	if(addChild(child, tab_group))
 323	{
 324		sendChildToBack(child);
 325		return true;
 326	}
 327
 328	return false;
 329}
 330
 331// remove the specified child from the view, and set it's parent to NULL.
 332void LLView::removeChild(LLView* child)
 333{
 334	//llassert_always(sDepth == 0); // Avoid re-ordering while drawing; it can cause subtle iterator bugs
 335	if (child->mParentView == this) 
 336	{
 337		// if we are removing an item we are currently iterating over, that would be bad
 338		llassert(child->mInDraw == false);
 339		mChildList.remove( child );
 340		child->mParentView = NULL;
 341		if (child->isCtrl())
 342		{
 343			child_tab_order_t::iterator found = mCtrlOrder.find(static_cast<LLUICtrl*>(child));
 344			if(found != mCtrlOrder.end())
 345			{
 346				mCtrlOrder.erase(found);
 347			}
 348		}
 349	}
 350	else
 351	{
 352		llwarns << child->getName() << "is not a child of " << getName() << llendl;
 353	}
 354	updateBoundingRect();
 355}
 356
 357LLView::ctrl_list_t LLView::getCtrlList() const
 358{
 359	ctrl_list_t controls;
 360	BOOST_FOREACH(LLView* viewp, mChildList)
 361	{
 362		if(viewp->isCtrl())
 363		{
 364			controls.push_back(static_cast<LLUICtrl*>(viewp));
 365		}
 366	}
 367	return controls;
 368}
 369
 370LLView::ctrl_list_t LLView::getCtrlListSorted() const
 371{
 372	ctrl_list_t controls = getCtrlList();
 373	std::sort(controls.begin(), controls.end(), LLCompareByTabOrder(mCtrlOrder));
 374	return controls;
 375}
 376
 377
 378// This method compares two LLViews by the tab order specified in the comparator object.  The
 379// code for this is a little convoluted because each argument can have four states:
 380// 1) not a control, 2) a control but not in the tab order, 3) a control in the tab order, 4) null
 381bool LLCompareByTabOrder::operator() (const LLView* const a, const LLView* const b) const
 382{
 383	S32 a_score = 0, b_score = 0;
 384	if(a) a_score--;
 385	if(b) b_score--;
 386	if(a && a->isCtrl()) a_score--;
 387	if(b && b->isCtrl()) b_score--;
 388	if(a_score == -2 && b_score == -2)
 389	{
 390		const LLUICtrl * const a_ctrl = static_cast<const LLUICtrl*>(a);
 391		const LLUICtrl * const b_ctrl = static_cast<const LLUICtrl*>(b);
 392		LLView::child_tab_order_const_iter_t a_found = mTabOrder.find(a_ctrl), b_found = mTabOrder.find(b_ctrl);
 393		if(a_found != mTabOrder.end()) a_score--;
 394		if(b_found != mTabOrder.end()) b_score--;
 395		if(a_score == -3 && b_score == -3)
 396		{
 397			// whew!  Once we're in here, they're both in the tab order, and we can compare based on that
 398			return compareTabOrders(a_found->second, b_found->second);
 399		}
 400	}
 401	return (a_score == b_score) ? a < b : a_score < b_score;
 402}
 403
 404BOOL LLView::isInVisibleChain() const
 405{
 406	BOOL visible = TRUE;
 407
 408	const LLView* viewp = this;
 409	while(viewp)
 410	{
 411		if (!viewp->getVisible())
 412		{
 413			visible = FALSE;
 414			break;
 415		}
 416		viewp = viewp->getParent();
 417	}
 418	
 419	return visible;
 420}
 421
 422BOOL LLView::isInEnabledChain() const
 423{
 424	BOOL enabled = TRUE;
 425
 426	const LLView* viewp = this;
 427	while(viewp)
 428	{
 429		if (!viewp->getEnabled())
 430		{
 431			enabled = FALSE;
 432			break;
 433		}
 434		viewp = viewp->getParent();
 435	}
 436	
 437	return enabled;
 438}
 439
 440static void buildPathname(std::ostream& out, const LLView* view)
 441{
 442	if (! (view && view->getParent()))
 443	{
 444		return; // Don't include root in the path.
 445	}
 446
 447	buildPathname(out, view->getParent());
 448
 449	// Build pathname into ostream on the way back from recursion.
 450	out << '/' << view->getName();
 451}
 452
 453std::string LLView::getPathname() const
 454{
 455	std::ostringstream out;
 456	buildPathname(out, this);
 457	return out.str();
 458}
 459
 460//static
 461std::string LLView::getPathname(const LLView* view)
 462{
 463    if (! view)
 464    {
 465        return "NULL";
 466    }
 467    return view->getPathname();
 468}
 469
 470// virtual
 471BOOL LLView::canFocusChildren() const
 472{
 473	return TRUE;
 474}
 475
 476//virtual
 477void LLView::setEnabled(BOOL enabled)
 478{
 479	mEnabled = enabled;
 480}
 481
 482//virtual
 483bool LLView::isAvailable() const
 484{
 485    return isInEnabledChain() && isInVisibleChain();
 486}
 487
 488//static
 489bool LLView::isAvailable(const LLView* view)
 490{
 491    return view && view->isAvailable();
 492}
 493
 494//virtual
 495BOOL LLView::setLabelArg( const std::string& key, const LLStringExplicit& text )
 496{
 497	return FALSE;
 498}
 499
 500//virtual
 501LLRect LLView::getSnapRect() const
 502{
 503	return mRect;
 504}
 505
 506//virtual
 507LLRect LLView::getRequiredRect()
 508{
 509	return mRect;
 510}
 511
 512BOOL LLView::focusNextRoot()
 513{
 514	LLView::child_list_t result = LLView::getFocusRootsQuery().run(this);
 515	return LLView::focusNext(result);
 516}
 517
 518BOOL LLView::focusPrevRoot()
 519{
 520	LLView::child_list_t result = LLView::getFocusRootsQuery().run(this);
 521	return LLView::focusPrev(result);
 522}
 523
 524// static
 525BOOL LLView::focusNext(LLView::child_list_t & result)
 526{
 527	LLView::child_list_iter_t focused = result.end();
 528	for(LLView::child_list_iter_t iter = result.begin();
 529		iter != result.end();
 530		++iter)
 531	{
 532		if(gFocusMgr.childHasKeyboardFocus(*iter))
 533		{
 534			focused = iter;
 535			break;
 536		}
 537	}
 538	LLView::child_list_iter_t next = focused;
 539	next = (next == result.end()) ? result.begin() : ++next;
 540	while(next != focused)
 541	{
 542		// wrap around to beginning if necessary
 543		if(next == result.end())
 544		{
 545			next = result.begin();
 546		}
 547		if((*next)->isCtrl())
 548		{
 549			LLUICtrl * ctrl = static_cast<LLUICtrl*>(*next);
 550			ctrl->setFocus(TRUE);
 551			ctrl->onTabInto();  
 552			gFocusMgr.triggerFocusFlash();
 553			return TRUE;
 554		}
 555		++next;
 556	}
 557	return FALSE;
 558}
 559
 560// static
 561BOOL LLView::focusPrev(LLView::child_list_t & result)
 562{
 563	LLView::child_list_reverse_iter_t focused = result.rend();
 564	for(LLView::child_list_reverse_iter_t iter = result.rbegin();
 565		iter != result.rend();
 566		++iter)
 567	{
 568		if(gFocusMgr.childHasKeyboardFocus(*iter))
 569		{
 570			focused = iter;
 571			break;
 572		}
 573	}
 574	LLView::child_list_reverse_iter_t next = focused;
 575	next = (next == result.rend()) ? result.rbegin() : ++next;
 576	while(next != focused)
 577	{
 578		// wrap around to beginning if necessary
 579		if(next == result.rend())
 580		{
 581			next = result.rbegin();
 582		}
 583		if((*next)->isCtrl())
 584		{
 585			LLUICtrl * ctrl = static_cast<LLUICtrl*>(*next);
 586			if (!ctrl->hasFocus())
 587			{
 588				ctrl->setFocus(TRUE);
 589				ctrl->onTabInto();  
 590				gFocusMgr.triggerFocusFlash();
 591			}
 592			return TRUE;
 593		}
 594		++next;
 595	}
 596	return FALSE;
 597}
 598
 599// delete all children. Override this function if you need to
 600// perform any extra clean up such as cached pointers to selected
 601// children, etc.
 602void LLView::deleteAllChildren()
 603{
 604	// clear out the control ordering
 605	mCtrlOrder.clear();
 606
 607	while (!mChildList.empty())
 608	{
 609		LLView* viewp = mChildList.front();
 610		delete viewp; // will remove the child from mChildList
 611	}
 612}
 613
 614void LLView::setAllChildrenEnabled(BOOL b)
 615{
 616	BOOST_FOREACH(LLView* viewp, mChildList)
 617	{
 618		viewp->setEnabled(b);
 619	}
 620}
 621
 622// virtual
 623void LLView::setVisible(BOOL visible)
 624{
 625	if ( mVisible != visible )
 626	{
 627		mVisible = visible;
 628
 629		// notify children of visibility change if root, or part of visible hierarchy
 630		if (!getParent() || getParent()->isInVisibleChain())
 631		{
 632			// tell all children of this view that the visibility may have changed
 633			dirtyRect();
 634			handleVisibilityChange( visible );
 635		}
 636		updateBoundingRect();
 637	}
 638}
 639
 640// virtual
 641void LLView::handleVisibilityChange ( BOOL new_visibility )
 642{
 643	BOOST_FOREACH(LLView* viewp, mChildList)
 644	{
 645		// only views that are themselves visible will have their overall visibility affected by their ancestors
 646		if (viewp->getVisible())
 647		{
 648			viewp->handleVisibilityChange ( new_visibility );
 649		}
 650	}
 651}
 652
 653// virtual
 654void LLView::translate(S32 x, S32 y)
 655{
 656	mRect.translate(x, y);
 657	updateBoundingRect();
 658}
 659
 660// virtual
 661BOOL LLView::canSnapTo(const LLView* other_view)
 662{
 663	return other_view != this && other_view->getVisible();
 664}
 665
 666// virtual
 667void LLView::setSnappedTo(const LLView* snap_view)
 668{
 669}
 670
 671BOOL LLView::handleHover(S32 x, S32 y, MASK mask)
 672{
 673	return childrenHandleHover( x, y, mask ) != NULL;
 674}
 675
 676void LLView::onMouseEnter(S32 x, S32 y, MASK mask)
 677{
 678	//llinfos << "Mouse entered " << getName() << llendl;
 679}
 680
 681void LLView::onMouseLeave(S32 x, S32 y, MASK mask)
 682{
 683	//llinfos << "Mouse left " << getName() << llendl;
 684}
 685
 686bool LLView::visibleAndContains(S32 local_x, S32 local_y)
 687{
 688	return sDrilldown(this, local_x, local_y)
 689		&& getVisible();
 690}
 691
 692bool LLView::visibleEnabledAndContains(S32 local_x, S32 local_y)
 693{
 694	return visibleAndContains(local_x, local_y)
 695		&& getEnabled();
 696}
 697
 698void LLView::logMouseEvent()
 699{
 700	if (sDebugMouseHandling)
 701	{
 702		sMouseHandlerMessage = std::string("/") + mName + sMouseHandlerMessage;
 703	}
 704}
 705
 706template <typename METHOD, typename CHARTYPE>
 707LLView* LLView::childrenHandleCharEvent(const std::string& desc, const METHOD& method,
 708										CHARTYPE c, MASK mask)
 709{
 710	if ( getVisible() && getEnabled() )
 711	{
 712		BOOST_FOREACH(LLView* viewp, mChildList)
 713		{
 714			if ((viewp->*method)(c, mask, TRUE))
 715			{
 716				if (LLView::sDebugKeys)
 717				{
 718					llinfos << desc << " handled by " << viewp->getName() << llendl;
 719				}
 720				return viewp;
 721			}
 722		}
 723	}
 724    return NULL;
 725}
 726
 727// XDATA might be MASK, or S32 clicks
 728template <typename METHOD, typename XDATA>
 729LLView* LLView::childrenHandleMouseEvent(const METHOD& method, S32 x, S32 y, XDATA extra, bool allow_mouse_block)
 730{
 731	BOOST_FOREACH(LLView* viewp, mChildList)
 732	{
 733		S32 local_x = x - viewp->getRect().mLeft;
 734		S32 local_y = y - viewp->getRect().mBottom;
 735
 736		if (!viewp->visibleEnabledAndContains(local_x, local_y))
 737		{
 738			continue;
 739		}
 740
 741		if ((viewp->*method)( local_x, local_y, extra )
 742			|| (allow_mouse_block && viewp->blockMouseEvent( local_x, local_y )))
 743		{
 744			viewp->logMouseEvent();
 745			return viewp;
 746		}
 747	}
 748	return NULL;
 749}
 750
 751LLView* LLView::childrenHandleToolTip(S32 x, S32 y, MASK mask)
 752{
 753	BOOST_FOREACH(LLView* viewp, mChildList)
 754	{
 755		S32 local_x = x - viewp->getRect().mLeft;
 756		S32 local_y = y - viewp->getRect().mBottom;
 757		// Differs from childrenHandleMouseEvent() in that we want to offer
 758		// tooltips even for disabled widgets.
 759		if(!viewp->visibleAndContains(local_x, local_y))
 760		{
 761			continue;
 762		}
 763
 764		if (viewp->handleToolTip(local_x, local_y, mask) 
 765			|| viewp->blockMouseEvent(local_x, local_y))
 766		{
 767			viewp->logMouseEvent();
 768			return viewp;
 769		}
 770	}
 771	return NULL;
 772}
 773
 774LLView* LLView::childrenHandleDragAndDrop(S32 x, S32 y, MASK mask,
 775									   BOOL drop,
 776									   EDragAndDropType cargo_type,
 777									   void* cargo_data,
 778									   EAcceptance* accept,
 779									   std::string& tooltip_msg)
 780{
 781	// default to not accepting drag and drop, will be overridden by handler
 782	*accept = ACCEPT_NO;
 783
 784	BOOST_FOREACH(LLView* viewp, mChildList)
 785	{
 786		S32 local_x = x - viewp->getRect().mLeft;
 787		S32 local_y = y - viewp->getRect().mBottom;
 788		if( !viewp->visibleEnabledAndContains(local_x, local_y))
 789		{
 790			continue;
 791		}
 792
 793		// Differs from childrenHandleMouseEvent() simply in that this virtual
 794		// method call diverges pretty radically from the usual (x, y, int).
 795		if (viewp->handleDragAndDrop(local_x, local_y, mask, drop,
 796									 cargo_type,
 797									 cargo_data,
 798									 accept,
 799									 tooltip_msg)
 800			|| viewp->blockMouseEvent(local_x, local_y))
 801		{
 802			return viewp;
 803		}
 804	}
 805	return NULL;
 806}
 807
 808LLView* LLView::childrenHandleHover(S32 x, S32 y, MASK mask)
 809{
 810	BOOST_FOREACH(LLView* viewp, mChildList)
 811	{
 812		S32 local_x = x - viewp->getRect().mLeft;
 813		S32 local_y = y - viewp->getRect().mBottom;
 814		if(!viewp->visibleEnabledAndContains(local_x, local_y))
 815		{
 816			continue;
 817		}
 818
 819		// This call differentiates this method from childrenHandleMouseEvent().
 820		LLUI::sWindow->setCursor(viewp->getHoverCursor());
 821
 822		if (viewp->handleHover(local_x, local_y, mask)
 823			|| viewp->blockMouseEvent(local_x, local_y))
 824		{
 825			viewp->logMouseEvent();
 826			return viewp;
 827		}
 828	}
 829	return NULL;
 830}
 831
 832LLView*	LLView::childFromPoint(S32 x, S32 y, bool recur)
 833{
 834	if (!getVisible())
 835		return false;
 836
 837	BOOST_FOREACH(LLView* viewp, mChildList)
 838	{
 839		S32 local_x = x - viewp->getRect().mLeft;
 840		S32 local_y = y - viewp->getRect().mBottom;
 841		if (!viewp->visibleAndContains(local_x, local_y))
 842		{
 843			continue;
 844		}
 845		// Here we've found the first (frontmost) visible child at this level
 846		// containing the specified point. Is the caller asking us to drill
 847		// down and return the innermost leaf child at this point, or just the
 848		// top-level child?
 849		if (recur)
 850		{
 851			LLView* leaf(viewp->childFromPoint(local_x, local_y, recur));
 852			// Maybe viewp is already a leaf LLView, or maybe it has children
 853			// but this particular (x, y) point falls between them. If the
 854			// recursive call returns non-NULL, great, use that; else just use
 855			// viewp.
 856			return leaf? leaf : viewp;
 857		}
 858		return viewp;
 859
 860	}
 861	return 0;
 862}
 863
 864BOOL LLView::handleToolTip(S32 x, S32 y, MASK mask)
 865{
 866	BOOL handled = FALSE;
 867
 868	// parents provide tooltips first, which are optionally
 869	// overridden by children, in case child is mouse_opaque
 870	std::string tooltip = getToolTip();
 871	if (!tooltip.empty())
 872	{
 873		// allow "scrubbing" over ui by showing next tooltip immediately
 874		// if previous one was still visible
 875		F32 timeout = LLToolTipMgr::instance().toolTipVisible() 
 876			? LLUI::sSettingGroups["config"]->getF32( "ToolTipFastDelay" )
 877			: LLUI::sSettingGroups["config"]->getF32( "ToolTipDelay" );
 878		LLToolTipMgr::instance().show(LLToolTip::Params()
 879			.message(tooltip)
 880			.sticky_rect(calcScreenRect())
 881			.delay_time(timeout));
 882
 883		handled = TRUE;
 884	}
 885
 886	// child tooltips will override our own
 887	LLView* child_handler = childrenHandleToolTip(x, y, mask);
 888	if (child_handler)
 889	{
 890		handled = TRUE;
 891	}
 892
 893	return handled;
 894}
 895BOOL LLView::handleKey(KEY key, MASK mask, BOOL called_from_parent)
 896{
 897	BOOL handled = FALSE;
 898
 899	if (getVisible() && getEnabled())
 900	{
 901		if( called_from_parent )
 902		{
 903			// Downward traversal
 904			handled = childrenHandleKey( key, mask ) != NULL;
 905		}
 906
 907		if (!handled)
 908		{
 909			handled = handleKeyHere( key, mask );
 910			if (handled && LLView::sDebugKeys)
 911			{
 912				llinfos << "Key handled by " << getName() << llendl;
 913			}
 914		}
 915	}
 916
 917	if( !handled && !called_from_parent && mParentView)
 918	{
 919		// Upward traversal
 920		handled = mParentView->handleKey( key, mask, FALSE );
 921	}
 922	return handled;
 923}
 924
 925// Called from handleKey()
 926// Handles key in this object.  Checking parents and children happens in handleKey()
 927BOOL LLView::handleKeyHere(KEY key, MASK mask)
 928{
 929	return FALSE;
 930}
 931
 932BOOL LLView::handleUnicodeChar(llwchar uni_char, BOOL called_from_parent)
 933{
 934	BOOL handled = FALSE;
 935
 936	if (getVisible() && getEnabled())
 937	{
 938		if( called_from_parent )
 939		{
 940			// Downward traversal
 941			handled = childrenHandleUnicodeChar( uni_char ) != NULL;
 942		}
 943
 944		if (!handled)
 945		{
 946			handled = handleUnicodeCharHere(uni_char);
 947			if (handled && LLView::sDebugKeys)
 948			{
 949				llinfos << "Unicode key handled by " << getName() << llendl;
 950			}
 951		}
 952	}
 953
 954	if (!handled && !called_from_parent && mParentView)
 955	{
 956		// Upward traversal
 957		handled = mParentView->handleUnicodeChar(uni_char, FALSE);
 958	}
 959
 960	return handled;
 961}
 962
 963
 964BOOL LLView::handleUnicodeCharHere(llwchar uni_char )
 965{
 966	return FALSE;
 967}
 968
 969
 970BOOL LLView::handleDragAndDrop(S32 x, S32 y, MASK mask, BOOL drop,
 971							   EDragAndDropType cargo_type, void* cargo_data,
 972							   EAcceptance* accept,
 973							   std::string& tooltip_msg)
 974{
 975	return childrenHandleDragAndDrop( x, y, mask, drop, cargo_type, cargo_data, accept, tooltip_msg) != NULL;
 976}
 977
 978void LLView::onMouseCaptureLost()
 979{
 980}
 981
 982BOOL LLView::hasMouseCapture()
 983{ 
 984	return gFocusMgr.getMouseCapture() == this; 
 985}
 986
 987BOOL LLView::handleMouseUp(S32 x, S32 y, MASK mask)
 988{
 989	return childrenHandleMouseUp( x, y, mask ) != NULL;
 990}
 991
 992BOOL LLView::handleMouseDown(S32 x, S32 y, MASK mask)
 993{
 994	return childrenHandleMouseDown( x, y, mask ) != NULL;
 995}
 996
 997BOOL LLView::handleDoubleClick(S32 x, S32 y, MASK mask)
 998{
 999	return childrenHandleDoubleClick( x, y, mask ) != NULL;
1000}
1001
1002BOOL LLView::handleScrollWheel(S32 x, S32 y, S32 clicks)
1003{
1004	return childrenHandleScrollWheel( x, y, clicks ) != NULL;
1005}
1006
1007BOOL LLView::handleRightMouseDown(S32 x, S32 y, MASK mask)
1008{
1009	return childrenHandleRightMouseDown( x, y, mask ) != NULL;
1010}
1011
1012BOOL LLView::handleRightMouseUp(S32 x, S32 y, MASK mask)
1013{
1014	return childrenHandleRightMouseUp( x, y, mask ) != NULL;
1015}
1016 
1017BOOL LLView::handleMiddleMouseDown(S32 x, S32 y, MASK mask)
1018{
1019	return childrenHandleMiddleMouseDown( x, y, mask ) != NULL;
1020}
1021
1022BOOL LLView::handleMiddleMouseUp(S32 x, S32 y, MASK mask)
1023{
1024	return childrenHandleMiddleMouseUp( x, y, mask ) != NULL;
1025}
1026
1027LLView* LLView::childrenHandleScrollWheel(S32 x, S32 y, S32 clicks)
1028{
1029	return childrenHandleMouseEvent(&LLView::handleScrollWheel, x, y, clicks, false);
1030}
1031
1032// Called during downward traversal
1033LLView* LLView::childrenHandleKey(KEY key, MASK mask)
1034{
1035	return childrenHandleCharEvent("Key", &LLView::handleKey, key, mask);
1036}
1037
1038// Called during downward traversal
1039LLView* LLView::childrenHandleUnicodeChar(llwchar uni_char)
1040{
1041	return childrenHandleCharEvent("Unicode character", &LLView::handleUnicodeCharWithDummyMask,
1042								   uni_char, MASK_NONE);
1043}
1044
1045LLView* LLView::childrenHandleMouseDown(S32 x, S32 y, MASK mask)
1046{
1047	return childrenHandleMouseEvent(&LLView::handleMouseDown, x, y, mask);
1048}
1049
1050LLView* LLView::childrenHandleRightMouseDown(S32 x, S32 y, MASK mask)
1051{
1052	return childrenHandleMouseEvent(&LLView::handleRightMouseDown, x, y, mask);
1053}
1054
1055LLView* LLView::childrenHandleMiddleMouseDown(S32 x, S32 y, MASK mask)
1056{
1057	return childrenHandleMouseEvent(&LLView::handleMiddleMouseDown, x, y, mask);
1058}
1059
1060LLView* LLView::childrenHandleDoubleClick(S32 x, S32 y, MASK mask)
1061{
1062	return childrenHandleMouseEvent(&LLView::handleDoubleClick, x, y, mask);
1063}
1064
1065LLView* LLView::childrenHandleMouseUp(S32 x, S32 y, MASK mask)
1066{
1067	return childrenHandleMouseEvent(&LLView::handleMouseUp, x, y, mask);
1068}
1069
1070LLView* LLView::childrenHandleRightMouseUp(S32 x, S32 y, MASK mask)
1071{
1072	return childrenHandleMouseEvent(&LLView::handleRightMouseUp, x, y, mask);
1073}
1074
1075LLView* LLView::childrenHandleMiddleMouseUp(S32 x, S32 y, MASK mask)
1076{
1077	return childrenHandleMouseEvent(&LLView::handleMiddleMouseUp, x, y, mask);
1078}
1079
1080void LLView::draw()
1081{
1082	drawChildren();
1083}
1084
1085void LLView::drawChildren()
1086{
1087	if (!mChildList.empty())
1088	{
1089		LLView* rootp = LLUI::getRootView();		
1090		++sDepth;
1091
1092		for (child_list_reverse_iter_t child_iter = mChildList.rbegin(); child_iter != mChildList.rend();)  // ++child_iter)
1093		{
1094			child_list_reverse_iter_t child = child_iter++;
1095			LLView *viewp = *child;
1096			
1097			if (viewp == NULL)
1098			{
1099				continue;
1100			}
1101
1102			if (viewp->getVisible() && viewp->getRect().isValid())
1103			{
1104				LLRect screen_rect = viewp->calcScreenRect();
1105				if ( rootp->getLocalRect().overlaps(screen_rect)  && LLUI::sDirtyRect.overlaps(screen_rect))
1106				{
1107					LLUI::pushMatrix();
1108					{
1109						LLUI::translate((F32)viewp->getRect().mLeft, (F32)viewp->getRect().mBottom, 0.f);
1110						// flag the fact we are in draw here, in case overridden draw() method attempts to remove this widget
1111						viewp->mInDraw = true;
1112						viewp->draw();
1113						viewp->mInDraw = false;
1114
1115						if (sDebugRects)
1116						{
1117							viewp->drawDebugRect();
1118
1119							// Check for bogus rectangle
1120							if (!getRect().isValid())
1121							{
1122								llwarns << "Bogus rectangle for " << getName() << " with " << mRect << llendl;
1123							}
1124						}
1125					}
1126					LLUI::popMatrix();
1127				}
1128			}
1129
1130		}
1131		--sDepth;
1132	}
1133}
1134
1135void LLView::dirtyRect()
1136{
1137	LLView* child = getParent();
1138	LLView* parent = child ? child->getParent() : NULL;
1139	LLView* cur = this;
1140	while (child && parent && parent->getParent())
1141	{ //find third to top-most view
1142		cur = child;
1143		child = parent;
1144		parent = parent->getParent();
1145	}
1146
1147	LLUI::dirtyRect(cur->calcScreenRect());
1148}
1149
1150//Draw a box for debugging.
1151void LLView::drawDebugRect()
1152{
1153	std::set<LLView*>::iterator preview_iter = std::find(sPreviewHighlightedElements.begin(), sPreviewHighlightedElements.end(), this);	// figure out if it's a previewed element
1154
1155	LLUI::pushMatrix();
1156	{
1157		// drawing solids requires texturing be disabled
1158		gGL.getTexUnit(0)->unbind(LLTexUnit::TT_TEXTURE);
1159
1160		if (getUseBoundingRect())
1161		{
1162			LLUI::translate((F32)mBoundingRect.mLeft - (F32)mRect.mLeft, (F32)mBoundingRect.mBottom - (F32)mRect.mBottom, 0.f);
1163		}
1164
1165		LLRect debug_rect = getUseBoundingRect() ? mBoundingRect : mRect;
1166
1167		// draw red rectangle for the border
1168		LLColor4 border_color(0.25f, 0.25f, 0.25f, 1.f);
1169		if(preview_iter != sPreviewHighlightedElements.end())
1170		{
1171			if(LLView::sPreviewClickedElement && this == sPreviewClickedElement)
1172			{
1173				border_color = LLColor4::red;
1174			}
1175			else
1176			{
1177				static LLUIColor scroll_highlighted_color = LLUIColorTable::instance().getColor("ScrollHighlightedColor");
1178				border_color = scroll_highlighted_color;
1179			}
1180		}
1181		else
1182		{
1183			border_color.mV[sDepth%3] = 1.f;
1184		}
1185
1186		gGL.color4fv( border_color.mV );
1187
1188		gGL.begin(LLRender::LINES);
1189			gGL.vertex2i(0, debug_rect.getHeight() - 1);
1190			gGL.vertex2i(0, 0);
1191
1192			gGL.vertex2i(0, 0);
1193			gGL.vertex2i(debug_rect.getWidth() - 1, 0);
1194
1195			gGL.vertex2i(debug_rect.getWidth() - 1, 0);
1196			gGL.vertex2i(debug_rect.getWidth() - 1, debug_rect.getHeight() - 1);
1197
1198			gGL.vertex2i(debug_rect.getWidth() - 1, debug_rect.getHeight() - 1);
1199			gGL.vertex2i(0, debug_rect.getHeight() - 1);
1200		gGL.end();
1201
1202		// Draw the name if it's not a leaf node or not in editing or preview mode
1203		if (mChildList.size()
1204			&& preview_iter == sPreviewHighlightedElements.end()
1205			&& sDebugRectsShowNames)
1206		{
1207			//char temp[256];
1208			S32 x, y;
1209			gGL.color4fv( border_color.mV );
1210			x = debug_rect.getWidth()/2;
1211			y = debug_rect.getHeight()/2;
1212			std::string debug_text = llformat("%s (%d x %d)", getName().c_str(),
1213										debug_rect.getWidth(), debug_rect.getHeight());
1214			LLFontGL::getFontSansSerifSmall()->renderUTF8(debug_text, 0, (F32)x, (F32)y, border_color,
1215												LLFontGL::HCENTER, LLFontGL::BASELINE, LLFontGL::NORMAL, LLFontGL::NO_SHADOW,
1216												S32_MAX, S32_MAX, NULL, FALSE);
1217		}
1218	}
1219	LLUI::popMatrix();
1220}
1221
1222void LLView::drawChild(LLView* childp, S32 x_offset, S32 y_offset, BOOL force_draw)
1223{
1224	if (childp && childp->getParent() == this)
1225	{
1226		++sDepth;
1227
1228		if ((childp->getVisible() && childp->getRect().isValid()) 
1229			|| force_draw)
1230		{
1231			gGL.matrixMode(LLRender::MM_MODELVIEW);
1232			LLUI::pushMatrix();
1233			{
1234				LLUI::translate((F32)childp->getRect().mLeft + x_offset, (F32)childp->getRect().mBottom + y_offset, 0.f);
1235				childp->draw();
1236			}
1237			LLUI::popMatrix();
1238		}
1239
1240		--sDepth;
1241	}
1242}
1243
1244
1245void LLView::reshape(S32 width, S32 height, BOOL called_from_parent)
1246{
1247	// compute how much things changed and apply reshape logic to children
1248	S32 delta_width = width - getRect().getWidth();
1249	S32 delta_height = height - getRect().getHeight();
1250
1251	if (delta_width || delta_height || sForceReshape)
1252	{
1253		// adjust our rectangle
1254		mRect.mRight = getRect().mLeft + width;
1255		mRect.mTop = getRect().mBottom + height;
1256
1257		// move child views according to reshape flags
1258		BOOST_FOREACH(LLView* viewp, mChildList)
1259		{
1260			LLRect child_rect( viewp->mRect );
1261
1262			if (viewp->followsRight() && viewp->followsLeft())
1263			{
1264				child_rect.mRight += delta_width;
1265			}
1266			else if (viewp->followsRight())
1267			{
1268				child_rect.mLeft += delta_width;
1269				child_rect.mRight += delta_width;
1270			}
1271			else if (viewp->followsLeft())
1272			{
1273				// left is 0, don't need to adjust coords
1274			}
1275			else
1276			{
1277				// BUG what to do when we don't follow anyone?
1278				// for now, same as followsLeft
1279			}
1280
1281			if (viewp->followsTop() && viewp->followsBottom())
1282			{
1283				child_rect.mTop += delta_height;
1284			}
1285			else if (viewp->followsTop())
1286			{
1287				child_rect.mTop += delta_height;
1288				child_rect.mBottom += delta_height;
1289			}
1290			else if (viewp->followsBottom())
1291			{
1292				// bottom is 0, so don't need to adjust coords
1293			}
1294			else
1295			{
1296				// BUG what to do when we don't follow?
1297				// for now, same as bottom
1298			}
1299
1300			S32 delta_x = child_rect.mLeft - viewp->getRect().mLeft;
1301			S32 delta_y = child_rect.mBottom - viewp->getRect().mBottom;
1302			viewp->translate( delta_x, delta_y );
1303			viewp->reshape(child_rect.getWidth(), child_rect.getHeight());
1304		}
1305	}
1306
1307	if (!called_from_parent)
1308	{
1309		if (mParentView)
1310		{
1311			mParentView->reshape(mParentView->getRect().getWidth(), mParentView->getRect().getHeight(), FALSE);
1312		}
1313	}
1314
1315	updateBoundingRect();
1316}
1317
1318LLRect LLView::calcBoundingRect()
1319{
1320	LLRect local_bounding_rect = LLRect::null;
1321
1322	BOOST_FOREACH(LLView* childp, mChildList)
1323	{
1324		// ignore invisible and "top" children when calculating bounding rect
1325		// such as combobox popups
1326		if (!childp->getVisible() || childp == gFocusMgr.getTopCtrl()) 
1327		{
1328			continue;
1329		}
1330
1331		LLRect child_bounding_rect = childp->getBoundingRect();
1332
1333		if (local_bounding_rect.isEmpty())
1334		{
1335			// start out with bounding rect equal to first visible child's bounding rect
1336			local_bounding_rect = child_bounding_rect;
1337		}
1338		else
1339		{
1340			// accumulate non-null children rectangles
1341			if (!child_bounding_rect.isEmpty())
1342			{
1343				local_bounding_rect.unionWith(child_bounding_rect);
1344			}
1345		}
1346	}
1347
1348	// convert to parent-relative coordinates
1349	local_bounding_rect.translate(mRect.mLeft, mRect.mBottom);
1350	return local_bounding_rect;
1351}
1352
1353
1354void LLView::updateBoundingRect()
1355{
1356	if (isDead()) return;
1357
1358	LLRect cur_rect = mBoundingRect;
1359
1360	if (getUseBoundingRect())
1361	{
1362		mBoundingRect = calcBoundingRect();
1363	}
1364	else
1365	{
1366		mBoundingRect = mRect;
1367	}
1368
1369	// give parent view a chance to resize, in case we just moved, for example
1370	if (getParent() && getParent()->getUseBoundingRect())
1371	{
1372		getParent()->updateBoundingRect();
1373	}
1374
1375	if (mBoundingRect != cur_rect)
1376	{
1377		dirtyRect();
1378	}
1379
1380}
1381
1382LLRect LLView::calcScreenRect() const
1383{
1384	LLRect screen_rect;
1385	localPointToScreen(0, 0, &screen_rect.mLeft, &screen_rect.mBottom);
1386	localPointToScreen(getRect().getWidth(), getRect().getHeight(), &screen_rect.mRight, &screen_rect.mTop);
1387	return screen_rect;
1388}
1389
1390LLRect LLView::calcScreenBoundingRect() const
1391{
1392	LLRect screen_rect;
1393	// get bounding rect, if used
1394	LLRect bounding_rect = getUseBoundingRect() ? mBoundingRect : mRect;
1395
1396	// convert to local coordinates, as defined by mRect
1397	bounding_rect.translate(-mRect.mLeft, -mRect.mBottom);
1398
1399	localPointToScreen(bounding_rect.mLeft, bounding_rect.mBottom, &screen_rect.mLeft, &screen_rect.mBottom);
1400	localPointToScreen(bounding_rect.mRight, bounding_rect.mTop, &screen_rect.mRight, &screen_rect.mTop);
1401	return screen_rect;
1402}
1403
1404LLRect LLView::getLocalBoundingRect() const
1405{
1406	LLRect local_bounding_rect = getBoundingRect();
1407	local_bounding_rect.translate(-mRect.mLeft, -mRect.mBottom);
1408
1409	return local_bounding_rect;
1410}
1411
1412
1413LLRect LLView::getLocalRect() const
1414{
1415	LLRect local_rect(0, getRect().getHeight(), getRect().getWidth(), 0);
1416	return local_rect;
1417}
1418
1419LLRect LLView::getLocalSnapRect() const
1420{
1421	LLRect local_snap_rect = getSnapRect();
1422	local_snap_rect.translate(-getRect().mLeft, -getRect().mBottom);
1423	return local_snap_rect;
1424}
1425
1426BOOL LLView::hasAncestor(const LLView* parentp) const
1427{
1428	if (!parentp)
1429	{
1430		return FALSE;
1431	}
1432
1433	LLView* viewp = getParent();
1434	while(viewp)
1435	{
1436		if (viewp == parentp)
1437		{
1438			return TRUE;
1439		}
1440		viewp = viewp->getParent();
1441	}
1442
1443	return FALSE;
1444}
1445
1446//-----------------------------------------------------------------------------
1447
1448BOOL LLView::childHasKeyboardFocus( const std::string& childname ) const
1449{
1450	LLView *focus = dynamic_cast<LLView *>(gFocusMgr.getKeyboardFocus());
1451	
1452	while (focus != NULL)
1453	{
1454		if (focus->getName() == childname)
1455		{
1456			return TRUE;
1457		}
1458		
1459		focus = focus->getParent();
1460	}
1461	
1462	return FALSE;
1463}
1464
1465//-----------------------------------------------------------------------------
1466
1467BOOL LLView::hasChild(const std::string& childname, BOOL recurse) const
1468{
1469	return findChildView(childname, recurse) != NULL;
1470}
1471
1472//-----------------------------------------------------------------------------
1473// getChildView()
1474//-----------------------------------------------------------------------------
1475LLView* LLView::getChildView(const std::string& name, BOOL recurse) const
1476{
1477	return getChild<LLView>(name, recurse);
1478}
1479
1480static LLFastTimer::DeclareTimer FTM_FIND_VIEWS("Find Widgets");
1481
1482LLView* LLView::findChildView(const std::string& name, BOOL recurse) const
1483{
1484	LLFastTimer ft(FTM_FIND_VIEWS);
1485	//richard: should we allow empty names?
1486	//if(name.empty())
1487	//	return NULL;
1488	// Look for direct children *first*
1489	BOOST_FOREACH(LLView* childp, mChildList)
1490	{
1491		llassert(childp);
1492		if (childp->getName() == name)
1493		{
1494			return childp;
1495		}
1496	}
1497	if (recurse)
1498	{
1499		// Look inside each child as well.
1500		BOOST_FOREACH(LLView* childp, mChildList)
1501		{
1502			llassert(childp);
1503			LLView* viewp = childp->findChildView(name, recurse);
1504			if ( viewp )
1505			{
1506				return viewp;
1507			}
1508		}
1509	}
1510	return NULL;
1511}
1512
1513BOOL LLView::parentPointInView(S32 x, S32 y, EHitTestType type) const 
1514{ 
1515	return (getUseBoundingRect() && type == HIT_TEST_USE_BOUNDING_RECT)
1516		? mBoundingRect.pointInRect( x, y ) 
1517		: mRect.pointInRect( x, y ); 
1518}
1519
1520BOOL LLView::pointInView(S32 x, S32 y, EHitTestType type) const 
1521{ 
1522	return (getUseBoundingRect() && type == HIT_TEST_USE_BOUNDING_RECT)
1523		? mBoundingRect.pointInRect( x + mRect.mLeft, y + mRect.mBottom ) 
1524		: mRect.localPointInRect( x, y ); 
1525}
1526
1527BOOL LLView::blockMouseEvent(S32 x, S32 y) const
1528{
1529	return mMouseOpaque && pointInView(x, y, HIT_TEST_IGNORE_BOUNDING_RECT);
1530}
1531
1532// virtual
1533void LLView::screenPointToLocal(S32 screen_x, S32 screen_y, S32* local_x, S32* local_y) const
1534{
1535	*local_x = screen_x - getRect().mLeft;
1536	*local_y = screen_y - getRect().mBottom;
1537
1538	const LLView* cur = this;
1539	while( cur->mParentView )
1540	{
1541		cur = cur->mParentView;
1542		*local_x -= cur->getRect().mLeft;
1543		*local_y -= cur->getRect().mBottom;
1544	}
1545}
1546
1547void LLView::localPointToScreen(S32 local_x, S32 local_y, S32* screen_x, S32* screen_y) const
1548{
1549	*screen_x = local_x + getRect().mLeft;
1550	*screen_y = local_y + getRect().mBottom;
1551
1552	const LLView* cur = this;
1553	while( cur->mParentView )
1554	{
1555		cur = cur->mParentView;
1556		*screen_x += cur->getRect().mLeft;
1557		*screen_y += cur->getRect().mBottom;
1558	}
1559}
1560
1561void LLView::screenRectToLocal(const LLRect& screen, LLRect* local) const
1562{
1563	*local = screen;
1564	local->translate( -getRect().mLeft, -getRect().mBottom );
1565
1566	const LLView* cur = this;
1567	while( cur->mParentView )
1568	{
1569		cur = cur->mParentView;
1570		local->translate( -cur->getRect().mLeft, -cur->getRect().mBottom );
1571	}
1572}
1573
1574void LLView::localRectToScreen(const LLRect& local, LLRect* screen) const
1575{
1576	*screen = local;
1577	screen->translate( getRect().mLeft, getRect().mBottom );
1578
1579	const LLView* cur = this;
1580	while( cur->mParentView )
1581	{
1582		cur = cur->mParentView;
1583		screen->translate( cur->getRect().mLeft, cur->getRect().mBottom );
1584	}
1585}
1586
1587LLView* LLView::getRootView()
1588{
1589	LLView* view = this;
1590	while( view->mParentView )
1591	{
1592		view = view->mParentView;
1593	}
1594	return view;
1595}
1596
1597LLView* LLView::findPrevSibling(LLView* child)
1598{
1599	child_list_t::iterator prev_it = std::find(mChildList.begin(), mChildList.end(), child);
1600	if (prev_it != mChildList.end() && prev_it != mChildList.begin())
1601	{
1602		return *(--prev_it);
1603	}
1604	return NULL;
1605}
1606
1607LLView* LLView::findNextSibling(LLView* child)
1608{
1609	child_list_t::iterator next_it = std::find(mChildList.begin(), mChildList.end(), child);
1610	if (next_it != mChildList.end())
1611	{
1612		next_it++;
1613	}
1614
1615	return (next_it != mChildList.end()) ? *next_it : NULL;
1616}
1617
1618
1619LLCoordGL getNeededTranslation(const LLRect& input, const LLRect& constraint, BOOL allow_partial_outside)
1620{
1621	LLCoordGL delta;
1622
1623	if (allow_partial_outside)
1624	{
1625		const S32 KEEP_ONSCREEN_PIXELS = 16;
1626
1627		if( input.mRight - KEEP_ONSCREEN_PIXELS < constraint.mLeft )
1628		{
1629			delta.mX = constraint.mLeft - (input.mRight - KEEP_ONSCREEN_PIXELS);
1630		}
1631		else
1632		if( input.mLeft + KEEP_ONSCREEN_PIXELS > constraint.mRight )
1633		{
1634			delta.mX = constraint.mRight - (input.mLeft + KEEP_ONSCREEN_PIXELS);
1635		}
1636
1637		if( input.mTop > constraint.mTop )
1638		{
1639			delta.mY = constraint.mTop - input.mTop;
1640		}
1641		else
1642		if( input.mTop - KEEP_ONSCREEN_PIXELS < constraint.mBottom )
1643		{
1644			delta.mY = constraint.mBottom - (input.mTop - KEEP_ONSCREEN_PIXELS);
1645		}
1646	}
1647	else
1648	{
1649		if( input.mLeft < constraint.mLeft )
1650		{
1651			delta.mX = constraint.mLeft - input.mLeft;
1652		}
1653		else
1654		if( input.mRight > constraint.mRight )
1655		{
1656			delta.mX = constraint.mRight - input.mRight;
1657			// compensate for left edge possible going off screen
1658			delta.mX += llmax( 0, input.getWidth() - constraint.getWidth() );
1659		}
1660
1661		if( input.mTop > constraint.mTop )
1662		{
1663			delta.mY = constraint.mTop - input.mTop;
1664		}
1665		else
1666		if( input.mBottom < constraint.mBottom )
1667		{
1668			delta.mY = constraint.mBottom - input.mBottom;
1669			// compensate for top edge possible going off screen
1670			delta.mY -= llmax( 0, input.getHeight() - constraint.getHeight() );
1671		}
1672	}
1673
1674	return delta;
1675}
1676
1677// Moves the view so that it is entirely inside of constraint.
1678// If the view will not fit because it's too big, aligns with the top and left.
1679// (Why top and left?  That's where the drag bars are for floaters.)
1680BOOL LLView::translateIntoRect(const LLRect& constraint, BOOL allow_partial_outside )
1681{
1682	LLCoordGL translation = getNeededTranslation(getRect(), constraint, allow_partial_outside);
1683
1684	if (translation.mX != 0 || translation.mY != 0)
1685	{
1686		translate(translation.mX, translation.mY);
1687		return TRUE;
1688	}
1689	return FALSE;
1690}
1691
1692// move this view into "inside" but not onto "exclude"
1693// NOTE: if this view is already contained in "inside", we ignore the "exclude" rect
1694BOOL LLView::translateIntoRectWithExclusion( const LLRect& inside, const LLRect& exclude, BOOL allow_partial_outside )
1695{
1696	LLCoordGL translation = getNeededTranslation(getRect(), inside, allow_partial_outside);
1697	
1698	if (translation.mX != 0 || translation.mY != 0)
1699	{
1700		// translate ourselves into constraint rect
1701		translate(translation.mX, translation.mY);
1702	
1703		// do we overlap with exclusion area?
1704		// keep moving in the same direction to the other side of the exclusion rect
1705		if (exclude.overlaps(getRect()))
1706		{
1707			// moving right
1708			if (translation.mX > 0)
1709			{
1710				translate(exclude.mRight - getRect().mLeft, 0);
1711			}
1712			// moving left
1713			else if (translation.mX < 0)
1714			{
1715				translate(exclude.mLeft - getRect().mRight, 0);
1716			}
1717
1718			// moving up
1719			if (translation.mY > 0)
1720			{
1721				translate(0, exclude.mTop - getRect().mBottom);
1722			}
1723			// moving down
1724			else if (translation.mY < 0)
1725			{
1726				translate(0, exclude.mBottom - getRect().mTop);
1727			}
1728		}
1729
1730		return TRUE;
1731	}
1732	return FALSE;
1733}
1734
1735
1736void LLView::centerWithin(const LLRect& bounds)
1737{
1738	S32 left   = bounds.mLeft + (bounds.getWidth() - getRect().getWidth()) / 2;
1739	S32 bottom = bounds.mBottom + (bounds.getHeight() - getRect().getHeight()) / 2;
1740
1741	translate( left - getRect().mLeft, bottom - getRect().mBottom );
1742}
1743
1744BOOL LLView::localPointToOtherView( S32 x, S32 y, S32 *other_x, S32 *other_y, const LLView* other_view) const
1745{
1746	const LLView* cur_view = this;
1747	const LLView* root_view = NULL;
1748
1749	while (cur_view)
1750	{
1751		if (cur_view == other_view)
1752		{
1753			*other_x = x;
1754			*other_y = y;
1755			return TRUE;
1756		}
1757
1758		x += cur_view->getRect().mLeft;
1759		y += cur_view->getRect().mBottom;
1760
1761		cur_view = cur_view->getParent();
1762		root_view = cur_view;
1763	}
1764
1765	// assuming common root between two views, chase other_view's parents up to root
1766	cur_view = other_view;
1767	while (cur_view)
1768	{
1769		x -= cur_view->getRect().mLeft;
1770		y -= cur_view->getRect().mBottom;
1771
1772		cur_view = cur_view->getParent();
1773
1774		if (cur_view == root_view)
1775		{
1776			*other_x = x;
1777			*other_y = y;
1778			return TRUE;
1779		}
1780	}
1781
1782	*other_x = x;
1783	*other_y = y;
1784	return FALSE;
1785}
1786
1787BOOL LLView::localRectToOtherView( const LLRect& local, LLRect* other, const LLView* other_view ) const
1788{
1789	LLRect cur_rect = local;
1790	const LLView* cur_view = this;
1791	const LLView* root_view = NULL;
1792
1793	while (cur_view)
1794	{
1795		if (cur_view == other_view)
1796		{
1797			*other = cur_rect;
1798			return TRUE;
1799		}
1800
1801		cur_rect.translate(cur_view->getRect().mLeft, cur_view->getRect().mBottom);
1802
1803		cur_view = cur_view->getParent();
1804		root_view = cur_view;
1805	}
1806
1807	// assuming common root between two views, chase other_view's parents up to root
1808	cur_view = other_view;
1809	while (cur_view)
1810	{
1811		cur_rect.translate(-cur_view->getRect().mLeft, -cur_view->getRect().mBottom);
1812
1813		cur_view = cur_view->getParent();
1814
1815		if (cur_view == root_view)
1816		{
1817			*other = cur_rect;
1818			return TRUE;
1819		}
1820	}
1821
1822	*other = cur_rect;
1823	return FALSE;
1824}
1825
1826// static
1827const LLCtrlQuery & LLView::getTabOrderQuery()
1828{
1829	static LLCtrlQuery query;
1830	if(query.getPreFilters().size() == 0) {
1831		query.addPreFilter(LLVisibleFilter::getInstance());
1832		query.addPreFilter(LLEnabledFilter::getInstance());
1833		query.addPreFilter(LLTabStopFilter::getInstance());
1834		query.addPostFilter(LLLeavesFilter::getInstance());
1835	}
1836	return query;
1837}
1838
1839// This class is only used internally by getFocusRootsQuery below. 
1840class LLFocusRootsFilter : public LLQueryFilter, public LLSingleton<LLFocusRootsFilter>
1841{
1842	/*virtual*/ filterResult_t operator() (const LLView* const view, const viewList_t & children) const 
1843	{
1844		return filterResult_t(view->isCtrl() && view->isFocusRoot(), !view->isFocusRoot());
1845	}
1846};
1847
1848// static
1849const LLCtrlQuery & LLView::getFocusRootsQuery()
1850{
1851	static LLCtrlQuery query;
1852	if(query.getPreFilters().size() == 0) {
1853		query.addPreFilter(LLVisibleFilter::getInstance());
1854		query.addPreFilter(LLEnabledFilter::getInstance());
1855		query.addPreFilter(LLFocusRootsFilter::getInstance());
1856		query.addPostFilter(LLRootsFilter::getInstance());
1857	}
1858	return query;
1859}
1860
1861
1862void	LLView::setShape(const LLRect& new_rect, bool by_user)
1863{
1864	handleReshape(new_rect, by_user);
1865}
1866
1867void LLView::handleReshape(const LLRect& new_rect, bool by_user)
1868{
1869	reshape(new_rect.getWidth(), new_rect.getHeight());
1870	translate(new_rect.mLeft - getRect().mLeft, new_rect.mBottom - getRect().mBottom);
1871}
1872
1873LLView* LLView::findSnapRect(LLRect& new_rect, const LLCoordGL& mouse_dir,
1874							 LLView::ESnapType snap_type, S32 threshold, S32 padding)
1875{
1876	new_rect = mRect;
1877	LLView* snap_view = NULL;
1878
1879	if (!mParentView)
1880	{
1881		return NULL;
1882	}
1883	
1884	S32 delta_x = 0;
1885	S32 delta_y = 0;
1886	if (mouse_dir.mX >= 0)
1887	{
1888		S32 new_right = mRect.mRight;
1889		LLView* view = findSnapEdge(new_right, mouse_dir, SNAP_RIGHT, snap_type, threshold, padding);
1890		delta_x = new_right - mRect.mRight;
1891		snap_view = view ? view : snap_view;
1892	}
1893
1894	if (mouse_dir.mX <= 0)
1895	{
1896		S32 new_left = mRect.mLeft;
1897		LLView* view = findSnapEdge(new_left, mouse_dir, SNAP_LEFT, snap_type, threshold, padding);
1898		delta_x = new_left - mRect.mLeft;
1899		snap_view = view ? view : snap_view;
1900	}
1901
1902	if (mouse_dir.mY >= 0)
1903	{
1904		S32 new_top = mRect.mTop;
1905		LLView* view = findSnapEdge(new_top, mouse_dir, SNAP_TOP, snap_type, threshold, padding);
1906		delta_y = new_top - mRect.mTop;
1907		snap_view = view ? view : snap_view;
1908	}
1909
1910	if (mouse_dir.mY <= 0)
1911	{
1912		S32 new_bottom = mRect.mBottom;
1913		LLView* view = findSnapEdge(new_bottom, mouse_dir, SNAP_BOTTOM, snap_type, threshold, padding);
1914		delta_y = new_bottom - mRect.mBottom;
1915		snap_view = view ? view : snap_view;
1916	}
1917
1918	new_rect.translate(delta_x, delta_y);
1919	return snap_view;
1920}
1921
1922LLView*	LLView::findSnapEdge(S32& new_edge_val, const LLCoordGL& mouse_dir, ESnapEdge snap_edge, ESnapType snap_type, S32 threshold, S32 padding)
1923{
1924	LLRect snap_rect = getSnapRect();
1925	S32 snap_pos = 0;
1926	switch(snap_edge)
1927	{
1928	case SNAP_LEFT:
1929		snap_pos = snap_rect.mLeft;
1930		break;
1931	case SNAP_RIGHT:
1932		snap_pos = snap_rect.mRight;
1933		break;
1934	case SNAP_TOP:
1935		snap_pos = snap_rect.mTop;
1936		break;
1937	case SNAP_BOTTOM:
1938		snap_pos = snap_rect.mBottom;
1939		break;
1940	}
1941
1942	if (!mParentView)
1943	{
1944		new_edge_val = snap_pos;
1945		return NULL;
1946	}
1947
1948	LLView* snap_view = NULL;
1949
1950	// If the view is near the edge of its parent, snap it to
1951	// the edge.
1952	LLRect test_rect = snap_rect;
1953	test_rect.stretch(padding);
1954
1955	S32 x_threshold = threshold;
1956	S32 y_threshold = threshold;
1957
1958	LLRect parent_local_snap_rect = mParentView->getLocalSnapRect();
1959
1960	if (snap_type == SNAP_PARENT || snap_type == SNAP_PARENT_AND_SIBLINGS)
1961	{
1962		switch(snap_edge)
1963		{
1964		case SNAP_RIGHT:
1965			if (llabs(parent_local_snap_rect.mRight - test_rect.mRight) <= x_threshold 
1966				&& (parent_local_snap_rect.mRight - test_rect.mRight) * mouse_dir.mX >= 0)
1967			{
1968				snap_pos = parent_local_snap_rect.mRight - padding;
1969				snap_view = mParentView;
1970				x_threshold = llabs(parent_local_snap_rect.mRight - test_rect.mRight);
1971			}
1972			break;
1973		case SNAP_LEFT:
1974			if (llabs(test_rect.mLeft - parent_local_snap_rect.mLeft) <= x_threshold 
1975				&& test_rect.mLeft * mouse_dir.mX <= 0)
1976			{
1977				snap_pos = parent_local_snap_rect.mLeft + padding;
1978				snap_view = mParentView;
1979				x_threshold = llabs(test_rect.mLeft - parent_local_snap_rect.mLeft);
1980			}
1981			break;
1982		case SNAP_BOTTOM:
1983			if (llabs(test_rect.mBottom - parent_local_snap_rect.mBottom) <= y_threshold 
1984				&& test_rect.mBottom * mouse_dir.mY <= 0)
1985			{
1986				snap_pos = parent_local_snap_rect.mBottom + padding;
1987				snap_view = mParentView;
1988				y_threshold = llabs(test_rect.mBottom - parent_local_snap_rect.mBottom);
1989			}
1990			break;
1991		case SNAP_TOP:
1992			if (llabs(parent_local_snap_rect.mTop - test_rect.mTop) <= y_threshold && (parent_local_snap_rect.mTop - test_rect.mTop) * mouse_dir.mY >= 0)
1993			{
1994				snap_pos = parent_local_snap_rect.mTop - padding;
1995				snap_view = mParentView;
1996				y_threshold = llabs(parent_local_snap_rect.mTop - test_rect.mTop);
1997			}
1998			break;
1999		default:
2000			llerrs << "Invalid snap edge" << llendl;
2001		}
2002	}
2003
2004	if (snap_type == SNAP_SIBLINGS || snap_type == SNAP_PARENT_AND_SIBLINGS)
2005	{
2006		for ( child_list_const_iter_t child_it = mParentView->getChildList()->begin();
2007			  child_it != mParentView->getChildList()->end(); ++child_it)
2008		{
2009			LLView* siblingp = *child_it;
2010
2011			if (!canSnapTo(siblingp)) continue;
2012
2013			LLRect sibling_rect = siblingp->getSnapRect();
2014
2015			switch(snap_edge)
2016			{
2017			case SNAP_RIGHT:
2018				if (llabs(test_rect.mRight - sibling_rect.mLeft) <= x_threshold 
2019					&& (test_rect.mRight - sibling_rect.mLeft) * mouse_dir.mX <= 0)
2020				{
2021					snap_pos = sibling_rect.mLeft - padding;
2022					snap_view = siblingp;
2023					x_threshold = llabs(test_rect.mRight - sibling_rect.mLeft);
2024				}
2025				// if snapped with sibling along other axis, check for shared edge
2026				else if (llabs(sibling_rect.mTop - (test_rect.mBottom - padding)) <= y_threshold 
2027					|| llabs(sibling_rect.mBottom - (test_rect.mTop + padding)) <= x_threshold)
2028				{
2029					if (llabs(test_rect.mRight - sibling_rect.mRight) <= x_threshold 
2030						&& (test_rect.mRight - sibling_rect.mRight) * mouse_dir.mX <= 0)
2031					{
2032						snap_pos = sibling_rect.mRight;
2033						snap_view = siblingp;
2034						x_threshold = llabs(test_rect.mRight - sibling_rect.mRight);
2035					}
2036				}
2037				break;
2038			case SNAP_LEFT:
2039				if (llabs(test_rect.mLeft - sibling_rect.mRight) <= x_threshold 
2040					&& (test_rect.mLeft - sibling_rect.mRight) * mouse_dir.mX <= 0)
2041				{
2042					snap_pos = sibling_rect.mRight + padding;
2043					snap_view = siblingp;
2044					x_threshold = llabs(test_rect.mLeft - sibling_rect.mRight);				
2045				}
2046				// if snapped with sibling along other axis, check for shared edge
2047				else if (llabs(sibling_rect.mTop - (test_rect.mBottom - padding)) <= y_threshold 
2048					|| llabs(sibling_rect.mBottom - (test_rect.mTop + padding)) <= y_threshold)
2049				{
2050					if (llabs(test_rect.mLeft - sibling_rect.mLeft) <= x_threshold 
2051						&& (test_rect.mLeft - siblin

Large files files are truncated, but you can click here to view the full file