PageRenderTime 72ms CodeModel.GetById 15ms RepoModel.GetById 0ms app.codeStats 1ms

/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

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. #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 - siblin

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