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