PageRenderTime 91ms CodeModel.GetById 16ms RepoModel.GetById 1ms app.codeStats 0ms

/indra/llui/llaccordionctrltab.cpp

https://bitbucket.org/lindenlab/viewer-beta/
C++ | 1068 lines | 852 code | 155 blank | 61 comment | 147 complexity | 79c6105c942f28a4128288f8f3ce26e4 MD5 | raw file
Possible License(s): LGPL-2.1
  1. /**
  2. * @file LLAccordionCtrlTab.cpp
  3. * @brief Collapsible control implementation
  4. *
  5. * $LicenseInfo:firstyear=2009&license=viewerlgpl$
  6. * Second Life Viewer Source Code
  7. * Copyright (C) 2010, Linden Research, Inc.
  8. *
  9. * This library is free software; you can redistribute it and/or
  10. * modify it under the terms of the GNU Lesser General Public
  11. * License as published by the Free Software Foundation;
  12. * version 2.1 of the License only.
  13. *
  14. * This library is distributed in the hope that it will be useful,
  15. * but WITHOUT ANY WARRANTY; without even the implied warranty of
  16. * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
  17. * Lesser General Public License for more details.
  18. *
  19. * You should have received a copy of the GNU Lesser General Public
  20. * License along with this library; if not, write to the Free Software
  21. * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA
  22. *
  23. * Linden Research, Inc., 945 Battery Street, San Francisco, CA 94111 USA
  24. * $/LicenseInfo$
  25. */
  26. #include "linden_common.h"
  27. #include "llaccordionctrltab.h"
  28. #include "llaccordionctrl.h"
  29. #include "lllocalcliprect.h"
  30. #include "llscrollbar.h"
  31. #include "lltextbox.h"
  32. #include "lltextutil.h"
  33. #include "lluictrl.h"
  34. static const std::string DD_BUTTON_NAME = "dd_button";
  35. static const std::string DD_TEXTBOX_NAME = "dd_textbox";
  36. static const std::string DD_HEADER_NAME = "dd_header";
  37. static const S32 HEADER_HEIGHT = 23;
  38. static const S32 HEADER_IMAGE_LEFT_OFFSET = 5;
  39. static const S32 HEADER_TEXT_LEFT_OFFSET = 30;
  40. static const F32 AUTO_OPEN_TIME = 1.f;
  41. static const S32 VERTICAL_MULTIPLE = 16;
  42. static const S32 PARENT_BORDER_MARGIN = 5;
  43. static LLDefaultChildRegistry::Register<LLAccordionCtrlTab> t1("accordion_tab");
  44. class LLAccordionCtrlTab::LLAccordionCtrlTabHeader : public LLUICtrl
  45. {
  46. public:
  47. friend class LLUICtrlFactory;
  48. struct Params : public LLInitParam::Block<Params, LLAccordionCtrlTab::Params>
  49. {
  50. Params();
  51. };
  52. LLAccordionCtrlTabHeader(const LLAccordionCtrlTabHeader::Params& p);
  53. virtual ~LLAccordionCtrlTabHeader();
  54. virtual void draw();
  55. virtual void reshape(S32 width, S32 height, BOOL called_from_parent = TRUE);
  56. virtual BOOL postBuild();
  57. std::string getTitle();
  58. void setTitle(const std::string& title, const std::string& hl);
  59. void setTitleFontStyle(std::string style);
  60. void setTitleColor(LLUIColor);
  61. void setSelected(bool is_selected) { mIsSelected = is_selected; }
  62. virtual void onMouseEnter(S32 x, S32 y, MASK mask);
  63. virtual void onMouseLeave(S32 x, S32 y, MASK mask);
  64. virtual BOOL handleKey(KEY key, MASK mask, BOOL called_from_parent);
  65. virtual BOOL handleDragAndDrop(S32 x, S32 y, MASK mask, BOOL drop,
  66. EDragAndDropType cargo_type,
  67. void* cargo_data,
  68. EAcceptance* accept,
  69. std::string& tooltip_msg);
  70. private:
  71. LLTextBox* mHeaderTextbox;
  72. // Overlay images (arrows)
  73. LLPointer<LLUIImage> mImageCollapsed;
  74. LLPointer<LLUIImage> mImageExpanded;
  75. LLPointer<LLUIImage> mImageCollapsedPressed;
  76. LLPointer<LLUIImage> mImageExpandedPressed;
  77. // Background images
  78. LLPointer<LLUIImage> mImageHeader;
  79. LLPointer<LLUIImage> mImageHeaderOver;
  80. LLPointer<LLUIImage> mImageHeaderPressed;
  81. LLPointer<LLUIImage> mImageHeaderFocused;
  82. // style saved when applying it in setTitleFontStyle
  83. LLStyle::Params mStyleParams;
  84. LLUIColor mHeaderBGColor;
  85. bool mNeedsHighlight;
  86. bool mIsSelected;
  87. LLFrameTimer mAutoOpenTimer;
  88. };
  89. LLAccordionCtrlTab::LLAccordionCtrlTabHeader::Params::Params()
  90. {
  91. }
  92. LLAccordionCtrlTab::LLAccordionCtrlTabHeader::LLAccordionCtrlTabHeader(
  93. const LLAccordionCtrlTabHeader::Params& p)
  94. : LLUICtrl(p)
  95. , mHeaderBGColor(p.header_bg_color())
  96. , mNeedsHighlight(false)
  97. , mIsSelected(false),
  98. mImageCollapsed(p.header_collapse_img),
  99. mImageCollapsedPressed(p.header_collapse_img_pressed),
  100. mImageExpanded(p.header_expand_img),
  101. mImageExpandedPressed(p.header_expand_img_pressed),
  102. mImageHeader(p.header_image),
  103. mImageHeaderOver(p.header_image_over),
  104. mImageHeaderPressed(p.header_image_pressed),
  105. mImageHeaderFocused(p.header_image_focused)
  106. {
  107. LLTextBox::Params textboxParams;
  108. textboxParams.name(DD_TEXTBOX_NAME);
  109. textboxParams.initial_value(p.title());
  110. textboxParams.text_color(p.header_text_color());
  111. textboxParams.follows.flags(FOLLOWS_NONE);
  112. textboxParams.font( p.font() );
  113. textboxParams.font_shadow(LLFontGL::NO_SHADOW);
  114. textboxParams.use_ellipses = true;
  115. textboxParams.bg_visible = false;
  116. textboxParams.mouse_opaque = false;
  117. textboxParams.parse_urls = false;
  118. mHeaderTextbox = LLUICtrlFactory::create<LLTextBox>(textboxParams);
  119. addChild(mHeaderTextbox);
  120. }
  121. LLAccordionCtrlTab::LLAccordionCtrlTabHeader::~LLAccordionCtrlTabHeader()
  122. {
  123. }
  124. BOOL LLAccordionCtrlTab::LLAccordionCtrlTabHeader::postBuild()
  125. {
  126. return TRUE;
  127. }
  128. std::string LLAccordionCtrlTab::LLAccordionCtrlTabHeader::getTitle()
  129. {
  130. if(mHeaderTextbox)
  131. {
  132. return mHeaderTextbox->getText();
  133. }
  134. else
  135. {
  136. return LLStringUtil::null;
  137. }
  138. }
  139. void LLAccordionCtrlTab::LLAccordionCtrlTabHeader::setTitle(const std::string& title, const std::string& hl)
  140. {
  141. if(mHeaderTextbox)
  142. {
  143. LLTextUtil::textboxSetHighlightedVal(
  144. mHeaderTextbox,
  145. mStyleParams,
  146. title,
  147. hl);
  148. }
  149. }
  150. void LLAccordionCtrlTab::LLAccordionCtrlTabHeader::setTitleFontStyle(std::string style)
  151. {
  152. if (mHeaderTextbox)
  153. {
  154. std::string text = mHeaderTextbox->getText();
  155. mStyleParams.font(mHeaderTextbox->getDefaultFont());
  156. mStyleParams.font.style(style);
  157. mHeaderTextbox->setText(text, mStyleParams);
  158. }
  159. }
  160. void LLAccordionCtrlTab::LLAccordionCtrlTabHeader::setTitleColor(LLUIColor color)
  161. {
  162. if(mHeaderTextbox)
  163. {
  164. mHeaderTextbox->setColor(color);
  165. }
  166. }
  167. void LLAccordionCtrlTab::LLAccordionCtrlTabHeader::draw()
  168. {
  169. S32 width = getRect().getWidth();
  170. S32 height = getRect().getHeight();
  171. F32 alpha = getCurrentTransparency();
  172. gl_rect_2d(0,0,width - 1 ,height - 1,mHeaderBGColor.get() % alpha,true);
  173. LLAccordionCtrlTab* parent = dynamic_cast<LLAccordionCtrlTab*>(getParent());
  174. bool collapsible = (parent && parent->getCollapsible());
  175. bool expanded = (parent && parent->getDisplayChildren());
  176. // Handle overlay images, if needed
  177. // Only show green "focus" background image if the accordion is open,
  178. // because the user's mental model of focus is that it goes away after
  179. // the accordion is closed.
  180. if (getParent()->hasFocus() || mIsSelected
  181. /*&& !(collapsible && !expanded)*/ // WHY??
  182. )
  183. {
  184. mImageHeaderFocused->draw(0,0,width,height);
  185. }
  186. else
  187. {
  188. mImageHeader->draw(0,0,width,height);
  189. }
  190. if(mNeedsHighlight)
  191. {
  192. mImageHeaderOver->draw(0,0,width,height);
  193. }
  194. if(collapsible)
  195. {
  196. LLPointer<LLUIImage> overlay_image;
  197. if(expanded)
  198. {
  199. overlay_image = mImageExpanded;
  200. }
  201. else
  202. {
  203. overlay_image = mImageCollapsed;
  204. }
  205. overlay_image->draw(HEADER_IMAGE_LEFT_OFFSET,
  206. (height - overlay_image->getHeight()) / 2);
  207. }
  208. LLUICtrl::draw();
  209. }
  210. void LLAccordionCtrlTab::LLAccordionCtrlTabHeader::reshape(S32 width, S32 height, BOOL called_from_parent /* = TRUE */)
  211. {
  212. S32 header_height = mHeaderTextbox->getTextPixelHeight();
  213. LLRect textboxRect(HEADER_TEXT_LEFT_OFFSET,(height+header_height)/2 ,width,(height-header_height)/2);
  214. mHeaderTextbox->reshape(textboxRect.getWidth(), textboxRect.getHeight());
  215. mHeaderTextbox->setRect(textboxRect);
  216. if (mHeaderTextbox->getTextPixelWidth() > mHeaderTextbox->getRect().getWidth())
  217. {
  218. setToolTip(mHeaderTextbox->getText());
  219. }
  220. else
  221. {
  222. setToolTip(LLStringUtil::null);
  223. }
  224. }
  225. void LLAccordionCtrlTab::LLAccordionCtrlTabHeader::onMouseEnter(S32 x, S32 y, MASK mask)
  226. {
  227. LLUICtrl::onMouseEnter(x, y, mask);
  228. mNeedsHighlight = true;
  229. }
  230. void LLAccordionCtrlTab::LLAccordionCtrlTabHeader::onMouseLeave(S32 x, S32 y, MASK mask)
  231. {
  232. LLUICtrl::onMouseLeave(x, y, mask);
  233. mNeedsHighlight = false;
  234. mAutoOpenTimer.stop();
  235. }
  236. BOOL LLAccordionCtrlTab::LLAccordionCtrlTabHeader::handleKey(KEY key, MASK mask, BOOL called_from_parent)
  237. {
  238. if ( ( key == KEY_LEFT || key == KEY_RIGHT) && mask == MASK_NONE)
  239. {
  240. return getParent()->handleKey(key, mask, called_from_parent);
  241. }
  242. return LLUICtrl::handleKey(key, mask, called_from_parent);
  243. }
  244. BOOL LLAccordionCtrlTab::LLAccordionCtrlTabHeader::handleDragAndDrop(S32 x, S32 y, MASK mask,
  245. BOOL drop,
  246. EDragAndDropType cargo_type,
  247. void* cargo_data,
  248. EAcceptance* accept,
  249. std::string& tooltip_msg)
  250. {
  251. LLAccordionCtrlTab* parent = dynamic_cast<LLAccordionCtrlTab*>(getParent());
  252. if ( parent && !parent->getDisplayChildren() && parent->getCollapsible() && parent->canOpenClose() )
  253. {
  254. if (mAutoOpenTimer.getStarted())
  255. {
  256. if (mAutoOpenTimer.getElapsedTimeF32() > AUTO_OPEN_TIME)
  257. {
  258. parent->changeOpenClose(false);
  259. mAutoOpenTimer.stop();
  260. return TRUE;
  261. }
  262. }
  263. else
  264. mAutoOpenTimer.start();
  265. }
  266. return LLUICtrl::handleDragAndDrop(x, y, mask, drop, cargo_type,
  267. cargo_data, accept, tooltip_msg);
  268. }
  269. LLAccordionCtrlTab::Params::Params()
  270. : title("title")
  271. ,display_children("expanded", true)
  272. ,header_height("header_height", HEADER_HEIGHT),
  273. min_width("min_width", 0),
  274. min_height("min_height", 0)
  275. ,collapsible("collapsible", true)
  276. ,header_bg_color("header_bg_color")
  277. ,dropdown_bg_color("dropdown_bg_color")
  278. ,header_visible("header_visible",true)
  279. ,padding_left("padding_left",2)
  280. ,padding_right("padding_right",2)
  281. ,padding_top("padding_top",2)
  282. ,padding_bottom("padding_bottom",2)
  283. ,header_expand_img("header_expand_img")
  284. ,header_expand_img_pressed("header_expand_img_pressed")
  285. ,header_collapse_img("header_collapse_img")
  286. ,header_collapse_img_pressed("header_collapse_img_pressed")
  287. ,header_image("header_image")
  288. ,header_image_over("header_image_over")
  289. ,header_image_pressed("header_image_pressed")
  290. ,header_image_focused("header_image_focused")
  291. ,header_text_color("header_text_color")
  292. ,fit_panel("fit_panel",true)
  293. ,selection_enabled("selection_enabled", false)
  294. {
  295. changeDefault(mouse_opaque, false);
  296. }
  297. LLAccordionCtrlTab::LLAccordionCtrlTab(const LLAccordionCtrlTab::Params&p)
  298. : LLUICtrl(p)
  299. ,mDisplayChildren(p.display_children)
  300. ,mCollapsible(p.collapsible)
  301. ,mExpandedHeight(0)
  302. ,mDropdownBGColor(p.dropdown_bg_color())
  303. ,mHeaderVisible(p.header_visible)
  304. ,mPaddingLeft(p.padding_left)
  305. ,mPaddingRight(p.padding_right)
  306. ,mPaddingTop(p.padding_top)
  307. ,mPaddingBottom(p.padding_bottom)
  308. ,mCanOpenClose(true)
  309. ,mFitPanel(p.fit_panel)
  310. ,mSelectionEnabled(p.selection_enabled)
  311. ,mContainerPanel(NULL)
  312. ,mScrollbar(NULL)
  313. {
  314. mStoredOpenCloseState = false;
  315. mWasStateStored = false;
  316. mDropdownBGColor = LLColor4::white;
  317. LLAccordionCtrlTabHeader::Params headerParams;
  318. headerParams.name(DD_HEADER_NAME);
  319. headerParams.title(p.title);
  320. mHeader = LLUICtrlFactory::create<LLAccordionCtrlTabHeader>(headerParams);
  321. addChild(mHeader, 1);
  322. LLFocusableElement::setFocusReceivedCallback(boost::bind(&LLAccordionCtrlTab::selectOnFocusReceived, this));
  323. if (!p.selection_enabled)
  324. {
  325. LLFocusableElement::setFocusLostCallback(boost::bind(&LLAccordionCtrlTab::deselectOnFocusLost, this));
  326. }
  327. reshape(100, 200,FALSE);
  328. }
  329. LLAccordionCtrlTab::~LLAccordionCtrlTab()
  330. {
  331. }
  332. void LLAccordionCtrlTab::setDisplayChildren(bool display)
  333. {
  334. mDisplayChildren = display;
  335. LLRect rect = getRect();
  336. rect.mBottom = rect.mTop - (getDisplayChildren() ?
  337. mExpandedHeight : HEADER_HEIGHT);
  338. setRect(rect);
  339. if(mContainerPanel)
  340. mContainerPanel->setVisible(getDisplayChildren());
  341. if(mDisplayChildren)
  342. {
  343. adjustContainerPanel();
  344. }
  345. else
  346. {
  347. if(mScrollbar)
  348. mScrollbar->setVisible(false);
  349. }
  350. }
  351. void LLAccordionCtrlTab::reshape(S32 width, S32 height, BOOL called_from_parent /* = TRUE */)
  352. {
  353. LLRect headerRect;
  354. headerRect.setLeftTopAndSize(
  355. 0,height,width,HEADER_HEIGHT);
  356. mHeader->setRect(headerRect);
  357. mHeader->reshape(headerRect.getWidth(), headerRect.getHeight());
  358. if(!mDisplayChildren)
  359. return;
  360. LLRect childRect;
  361. childRect.setLeftTopAndSize(
  362. getPaddingLeft(),
  363. height - getHeaderHeight() - getPaddingTop(),
  364. width - getPaddingLeft() - getPaddingRight(),
  365. height - getHeaderHeight() - getPaddingTop() - getPaddingBottom() );
  366. adjustContainerPanel(childRect);
  367. }
  368. void LLAccordionCtrlTab::changeOpenClose(bool is_open)
  369. {
  370. if(is_open)
  371. mExpandedHeight = getRect().getHeight();
  372. setDisplayChildren(!is_open);
  373. reshape(getRect().getWidth(), getRect().getHeight(), FALSE);
  374. if (mCommitSignal)
  375. {
  376. (*mCommitSignal)(this, getDisplayChildren());
  377. }
  378. }
  379. void LLAccordionCtrlTab::handleVisibilityChange(BOOL new_visibility)
  380. {
  381. LLUICtrl::handleVisibilityChange(new_visibility);
  382. notifyParent(LLSD().with("child_visibility_change", new_visibility));
  383. }
  384. BOOL LLAccordionCtrlTab::handleMouseDown(S32 x, S32 y, MASK mask)
  385. {
  386. if(mCollapsible && mHeaderVisible && mCanOpenClose)
  387. {
  388. if(y >= (getRect().getHeight() - HEADER_HEIGHT) )
  389. {
  390. mHeader->setFocus(true);
  391. changeOpenClose(getDisplayChildren());
  392. //reset stored state
  393. mWasStateStored = false;
  394. return TRUE;
  395. }
  396. }
  397. return LLUICtrl::handleMouseDown(x,y,mask);
  398. }
  399. BOOL LLAccordionCtrlTab::handleMouseUp(S32 x, S32 y, MASK mask)
  400. {
  401. return LLUICtrl::handleMouseUp(x,y,mask);
  402. }
  403. boost::signals2::connection LLAccordionCtrlTab::setDropDownStateChangedCallback(commit_callback_t cb)
  404. {
  405. return setCommitCallback(cb);
  406. }
  407. bool LLAccordionCtrlTab::addChild(LLView* child, S32 tab_group)
  408. {
  409. if(DD_HEADER_NAME != child->getName())
  410. {
  411. reshape(child->getRect().getWidth() , child->getRect().getHeight() + HEADER_HEIGHT );
  412. mExpandedHeight = getRect().getHeight();
  413. }
  414. bool res = LLUICtrl::addChild(child, tab_group);
  415. if(DD_HEADER_NAME != child->getName())
  416. {
  417. if(!mCollapsible)
  418. setDisplayChildren(true);
  419. else
  420. setDisplayChildren(getDisplayChildren());
  421. }
  422. if (!mContainerPanel)
  423. mContainerPanel = findContainerView();
  424. return res;
  425. }
  426. void LLAccordionCtrlTab::setAccordionView(LLView* panel)
  427. {
  428. addChild(panel,0);
  429. }
  430. std::string LLAccordionCtrlTab::getTitle() const
  431. {
  432. if (mHeader)
  433. {
  434. return mHeader->getTitle();
  435. }
  436. else
  437. {
  438. return LLStringUtil::null;
  439. }
  440. }
  441. void LLAccordionCtrlTab::setTitle(const std::string& title, const std::string& hl)
  442. {
  443. if (mHeader)
  444. {
  445. mHeader->setTitle(title, hl);
  446. }
  447. }
  448. void LLAccordionCtrlTab::setTitleFontStyle(std::string style)
  449. {
  450. if (mHeader)
  451. {
  452. mHeader->setTitleFontStyle(style);
  453. }
  454. }
  455. void LLAccordionCtrlTab::setTitleColor(LLUIColor color)
  456. {
  457. if (mHeader)
  458. {
  459. mHeader->setTitleColor(color);
  460. }
  461. }
  462. boost::signals2::connection LLAccordionCtrlTab::setFocusReceivedCallback(const focus_signal_t::slot_type& cb)
  463. {
  464. if (mHeader)
  465. {
  466. return mHeader->setFocusReceivedCallback(cb);
  467. }
  468. return boost::signals2::connection();
  469. }
  470. boost::signals2::connection LLAccordionCtrlTab::setFocusLostCallback(const focus_signal_t::slot_type& cb)
  471. {
  472. if (mHeader)
  473. {
  474. return mHeader->setFocusLostCallback(cb);
  475. }
  476. return boost::signals2::connection();
  477. }
  478. void LLAccordionCtrlTab::setSelected(bool is_selected)
  479. {
  480. if (mHeader)
  481. {
  482. mHeader->setSelected(is_selected);
  483. }
  484. }
  485. LLView* LLAccordionCtrlTab::findContainerView()
  486. {
  487. for(child_list_const_iter_t it = getChildList()->begin();
  488. getChildList()->end() != it; ++it)
  489. {
  490. LLView* child = *it;
  491. if(DD_HEADER_NAME == child->getName())
  492. continue;
  493. if(!child->getVisible())
  494. continue;
  495. return child;
  496. }
  497. return NULL;
  498. }
  499. void LLAccordionCtrlTab::selectOnFocusReceived()
  500. {
  501. if (getParent()) // A parent may not be set if tabs are added dynamically.
  502. getParent()->notifyParent(LLSD().with("action", "select_current"));
  503. }
  504. void LLAccordionCtrlTab::deselectOnFocusLost()
  505. {
  506. if(getParent()) // A parent may not be set if tabs are added dynamically.
  507. {
  508. getParent()->notifyParent(LLSD().with("action", "deselect_current"));
  509. }
  510. }
  511. S32 LLAccordionCtrlTab::getHeaderHeight()
  512. {
  513. return mHeaderVisible?HEADER_HEIGHT:0;
  514. }
  515. void LLAccordionCtrlTab::setHeaderVisible(bool value)
  516. {
  517. if(mHeaderVisible == value)
  518. return;
  519. mHeaderVisible = value;
  520. if(mHeader)
  521. mHeader->setVisible(value);
  522. reshape(getRect().getWidth(), getRect().getHeight(), FALSE);
  523. };
  524. //virtual
  525. BOOL LLAccordionCtrlTab::postBuild()
  526. {
  527. if(mHeader)
  528. mHeader->setVisible(mHeaderVisible);
  529. static LLUICachedControl<S32> scrollbar_size ("UIScrollbarSize", 0);
  530. LLRect scroll_rect;
  531. scroll_rect.setOriginAndSize(
  532. getRect().getWidth() - scrollbar_size,
  533. 1,
  534. scrollbar_size,
  535. getRect().getHeight() - 1);
  536. mContainerPanel = findContainerView();
  537. if(!mFitPanel)
  538. {
  539. LLScrollbar::Params sbparams;
  540. sbparams.name("scrollable vertical");
  541. sbparams.rect(scroll_rect);
  542. sbparams.orientation(LLScrollbar::VERTICAL);
  543. sbparams.doc_size(getRect().getHeight());
  544. sbparams.doc_pos(0);
  545. sbparams.page_size(getRect().getHeight());
  546. sbparams.step_size(VERTICAL_MULTIPLE);
  547. sbparams.follows.flags(FOLLOWS_RIGHT | FOLLOWS_TOP | FOLLOWS_BOTTOM);
  548. sbparams.change_callback(boost::bind(&LLAccordionCtrlTab::onScrollPosChangeCallback, this, _1, _2));
  549. mScrollbar = LLUICtrlFactory::create<LLScrollbar> (sbparams);
  550. LLView::addChild( mScrollbar );
  551. mScrollbar->setFollowsRight();
  552. mScrollbar->setFollowsTop();
  553. mScrollbar->setFollowsBottom();
  554. mScrollbar->setVisible(false);
  555. }
  556. if(mContainerPanel)
  557. mContainerPanel->setVisible(mDisplayChildren);
  558. return LLUICtrl::postBuild();
  559. }
  560. bool LLAccordionCtrlTab::notifyChildren (const LLSD& info)
  561. {
  562. if(info.has("action"))
  563. {
  564. std::string str_action = info["action"];
  565. if(str_action == "store_state")
  566. {
  567. storeOpenCloseState();
  568. return true;
  569. }
  570. if(str_action == "restore_state")
  571. {
  572. restoreOpenCloseState();
  573. return true;
  574. }
  575. }
  576. return LLUICtrl::notifyChildren(info);
  577. }
  578. S32 LLAccordionCtrlTab::notifyParent(const LLSD& info)
  579. {
  580. if(info.has("action"))
  581. {
  582. std::string str_action = info["action"];
  583. if(str_action == "size_changes")
  584. {
  585. //
  586. S32 height = info["height"];
  587. height = llmax(height,10) + HEADER_HEIGHT + getPaddingTop() + getPaddingBottom();
  588. mExpandedHeight = height;
  589. if(isExpanded())
  590. {
  591. LLRect panel_rect = getRect();
  592. panel_rect.setLeftTopAndSize( panel_rect.mLeft, panel_rect.mTop, panel_rect.getWidth(), height);
  593. reshape(getRect().getWidth(),height);
  594. setRect(panel_rect);
  595. }
  596. //LLAccordionCtrl should rearrange accordion tab if one of accordion change its size
  597. if (getParent()) // A parent may not be set if tabs are added dynamically.
  598. getParent()->notifyParent(info);
  599. return 1;
  600. }
  601. else if(str_action == "select_prev")
  602. {
  603. showAndFocusHeader();
  604. return 1;
  605. }
  606. }
  607. else if (info.has("scrollToShowRect"))
  608. {
  609. LLAccordionCtrl* parent = dynamic_cast<LLAccordionCtrl*>(getParent());
  610. if (parent && parent->getFitParent())
  611. {
  612. // EXT-8285 ('No attachments worn' text appears at the bottom of blank 'Attachments' accordion)
  613. // The problem was in passing message "scrollToShowRect" IN LLAccordionCtrlTab::notifyParent
  614. // FROM child LLScrollContainer TO parent LLAccordionCtrl with "it_parent" set to true.
  615. // It is wrong notification for parent accordion which leads to recursive call of adjustContainerPanel
  616. // As the result of recursive call of adjustContainerPanel we got LLAccordionCtrlTab
  617. // that reshaped and re-sized with different rectangles.
  618. // LLAccordionCtrl has own scrollContainer and LLAccordionCtrlTab has own scrollContainer
  619. // both should handle own scroll container's event.
  620. // So, if parent accordion "fit_parent" accordion tab should handle its scroll container events itself.
  621. return 1;
  622. }
  623. if (!getDisplayChildren())
  624. {
  625. // Don't pass scrolling event further if our contents are invisible (STORM-298).
  626. return 1;
  627. }
  628. }
  629. return LLUICtrl::notifyParent(info);
  630. }
  631. S32 LLAccordionCtrlTab::notify(const LLSD& info)
  632. {
  633. if(info.has("action"))
  634. {
  635. std::string str_action = info["action"];
  636. if(str_action == "select_first")
  637. {
  638. showAndFocusHeader();
  639. return 1;
  640. }
  641. else if( str_action == "select_last" )
  642. {
  643. if(getDisplayChildren() == false)
  644. {
  645. showAndFocusHeader();
  646. }
  647. else
  648. {
  649. LLView* view = getAccordionView();
  650. if(view)
  651. view->notify(LLSD().with("action","select_last"));
  652. }
  653. }
  654. }
  655. return 0;
  656. }
  657. BOOL LLAccordionCtrlTab::handleKey(KEY key, MASK mask, BOOL called_from_parent)
  658. {
  659. if( !mHeader->hasFocus() )
  660. return LLUICtrl::handleKey(key, mask, called_from_parent);
  661. if ( (key == KEY_RETURN )&& mask == MASK_NONE)
  662. {
  663. changeOpenClose(getDisplayChildren());
  664. return TRUE;
  665. }
  666. if ( (key == KEY_ADD || key == KEY_RIGHT)&& mask == MASK_NONE)
  667. {
  668. if(getDisplayChildren() == false)
  669. {
  670. changeOpenClose(getDisplayChildren());
  671. return TRUE;
  672. }
  673. }
  674. if ( (key == KEY_SUBTRACT || key == KEY_LEFT)&& mask == MASK_NONE)
  675. {
  676. if(getDisplayChildren() == true)
  677. {
  678. changeOpenClose(getDisplayChildren());
  679. return TRUE;
  680. }
  681. }
  682. if ( key == KEY_DOWN && mask == MASK_NONE)
  683. {
  684. //if collapsed go to the next accordion
  685. if(getDisplayChildren() == false)
  686. //we processing notifyParent so let call parent directly
  687. getParent()->notifyParent(LLSD().with("action","select_next"));
  688. else
  689. {
  690. getAccordionView()->notify(LLSD().with("action","select_first"));
  691. }
  692. return TRUE;
  693. }
  694. if ( key == KEY_UP && mask == MASK_NONE)
  695. {
  696. //go to the previous accordion
  697. //we processing notifyParent so let call parent directly
  698. getParent()->notifyParent(LLSD().with("action","select_prev"));
  699. return TRUE;
  700. }
  701. return LLUICtrl::handleKey(key, mask, called_from_parent);
  702. }
  703. void LLAccordionCtrlTab::showAndFocusHeader()
  704. {
  705. mHeader->setFocus(true);
  706. mHeader->setSelected(mSelectionEnabled);
  707. LLRect screen_rc;
  708. LLRect selected_rc = mHeader->getRect();
  709. localRectToScreen(selected_rc, &screen_rc);
  710. // This call to notifyParent() is intended to deliver "scrollToShowRect" command
  711. // to the parent LLAccordionCtrl so by calling it from the direct parent of this
  712. // accordion tab (assuming that the parent is an LLAccordionCtrl) the calls chain
  713. // is shortened and messages from inside the collapsed tabs are avoided.
  714. // See STORM-536.
  715. getParent()->notifyParent(LLSD().with("scrollToShowRect",screen_rc.getValue()));
  716. }
  717. void LLAccordionCtrlTab::storeOpenCloseState()
  718. {
  719. if(mWasStateStored)
  720. return;
  721. mStoredOpenCloseState = getDisplayChildren();
  722. mWasStateStored = true;
  723. }
  724. void LLAccordionCtrlTab::restoreOpenCloseState()
  725. {
  726. if(!mWasStateStored)
  727. return;
  728. if(getDisplayChildren() != mStoredOpenCloseState)
  729. {
  730. changeOpenClose(getDisplayChildren());
  731. }
  732. mWasStateStored = false;
  733. }
  734. void LLAccordionCtrlTab::adjustContainerPanel ()
  735. {
  736. S32 width = getRect().getWidth();
  737. S32 height = getRect().getHeight();
  738. LLRect child_rect;
  739. child_rect.setLeftTopAndSize(
  740. getPaddingLeft(),
  741. height - getHeaderHeight() - getPaddingTop(),
  742. width - getPaddingLeft() - getPaddingRight(),
  743. height - getHeaderHeight() - getPaddingTop() - getPaddingBottom() );
  744. adjustContainerPanel(child_rect);
  745. }
  746. void LLAccordionCtrlTab::adjustContainerPanel(const LLRect& child_rect)
  747. {
  748. if(!mContainerPanel)
  749. return;
  750. if(!mFitPanel)
  751. {
  752. show_hide_scrollbar(child_rect);
  753. updateLayout(child_rect);
  754. }
  755. else
  756. {
  757. mContainerPanel->reshape(child_rect.getWidth(),child_rect.getHeight());
  758. mContainerPanel->setRect(child_rect);
  759. }
  760. }
  761. S32 LLAccordionCtrlTab::getChildViewHeight()
  762. {
  763. if(!mContainerPanel)
  764. return 0;
  765. return mContainerPanel->getRect().getHeight();
  766. }
  767. void LLAccordionCtrlTab::show_hide_scrollbar(const LLRect& child_rect)
  768. {
  769. if(getChildViewHeight() > child_rect.getHeight() )
  770. showScrollbar(child_rect);
  771. else
  772. hideScrollbar(child_rect);
  773. }
  774. void LLAccordionCtrlTab::showScrollbar(const LLRect& child_rect)
  775. {
  776. if(!mContainerPanel || !mScrollbar)
  777. return;
  778. bool was_visible = mScrollbar->getVisible();
  779. mScrollbar->setVisible(true);
  780. static LLUICachedControl<S32> scrollbar_size ("UIScrollbarSize", 0);
  781. {
  782. ctrlSetLeftTopAndSize(mScrollbar,child_rect.getWidth()-scrollbar_size,
  783. child_rect.getHeight()-PARENT_BORDER_MARGIN,
  784. scrollbar_size,
  785. child_rect.getHeight()-2*PARENT_BORDER_MARGIN);
  786. }
  787. LLRect orig_rect = mContainerPanel->getRect();
  788. mScrollbar->setPageSize(child_rect.getHeight());
  789. mScrollbar->setDocParams(orig_rect.getHeight(),mScrollbar->getDocPos());
  790. if(was_visible)
  791. {
  792. S32 scroll_pos = llmin(mScrollbar->getDocPos(), orig_rect.getHeight() - child_rect.getHeight() - 1);
  793. mScrollbar->setDocPos(scroll_pos);
  794. }
  795. else//shrink child panel
  796. {
  797. updateLayout(child_rect);
  798. }
  799. }
  800. void LLAccordionCtrlTab::hideScrollbar( const LLRect& child_rect )
  801. {
  802. if(!mContainerPanel || !mScrollbar)
  803. return;
  804. if(mScrollbar->getVisible() == false)
  805. return;
  806. mScrollbar->setVisible(false);
  807. mScrollbar->setDocPos(0);
  808. //shrink child panel
  809. updateLayout(child_rect);
  810. }
  811. void LLAccordionCtrlTab::onScrollPosChangeCallback(S32, LLScrollbar*)
  812. {
  813. LLRect child_rect;
  814. S32 width = getRect().getWidth();
  815. S32 height = getRect().getHeight();
  816. child_rect.setLeftTopAndSize(
  817. getPaddingLeft(),
  818. height - getHeaderHeight() - getPaddingTop(),
  819. width - getPaddingLeft() - getPaddingRight(),
  820. height - getHeaderHeight() - getPaddingTop() - getPaddingBottom() );
  821. updateLayout(child_rect);
  822. }
  823. void LLAccordionCtrlTab::drawChild(const LLRect& root_rect,LLView* child)
  824. {
  825. if (child && child->getVisible() && child->getRect().isValid())
  826. {
  827. LLRect screen_rect;
  828. localRectToScreen(child->getRect(),&screen_rect);
  829. if ( root_rect.overlaps(screen_rect) && LLUI::sDirtyRect.overlaps(screen_rect))
  830. {
  831. gGL.matrixMode(LLRender::MM_MODELVIEW);
  832. LLUI::pushMatrix();
  833. {
  834. LLUI::translate((F32)child->getRect().mLeft, (F32)child->getRect().mBottom, 0.f);
  835. child->draw();
  836. }
  837. LLUI::popMatrix();
  838. }
  839. }
  840. }
  841. void LLAccordionCtrlTab::draw()
  842. {
  843. if(mFitPanel)
  844. LLUICtrl::draw();
  845. else
  846. {
  847. LLRect root_rect = getRootView()->getRect();
  848. drawChild(root_rect,mHeader);
  849. drawChild(root_rect,mScrollbar );
  850. {
  851. LLRect child_rect;
  852. S32 width = getRect().getWidth();
  853. S32 height = getRect().getHeight();
  854. child_rect.setLeftTopAndSize(
  855. getPaddingLeft(),
  856. height - getHeaderHeight() - getPaddingTop(),
  857. width - getPaddingLeft() - getPaddingRight(),
  858. height - getHeaderHeight() - getPaddingTop() - getPaddingBottom() );
  859. LLLocalClipRect clip(child_rect);
  860. drawChild(root_rect,mContainerPanel);
  861. }
  862. }
  863. }
  864. void LLAccordionCtrlTab::updateLayout ( const LLRect& child_rect )
  865. {
  866. LLView* child = getAccordionView();
  867. if(!mContainerPanel)
  868. return;
  869. S32 panel_top = child_rect.getHeight();
  870. S32 panel_width = child_rect.getWidth();
  871. static LLUICachedControl<S32> scrollbar_size ("UIScrollbarSize", 0);
  872. if(mScrollbar && mScrollbar->getVisible() != false)
  873. {
  874. panel_top+=mScrollbar->getDocPos();
  875. panel_width-=scrollbar_size;
  876. }
  877. //set sizes for first panels and dragbars
  878. LLRect panel_rect = child->getRect();
  879. ctrlSetLeftTopAndSize(mContainerPanel,child_rect.mLeft,panel_top,panel_width,panel_rect.getHeight());
  880. }
  881. void LLAccordionCtrlTab::ctrlSetLeftTopAndSize(LLView* panel, S32 left, S32 top, S32 width, S32 height)
  882. {
  883. if(!panel)
  884. return;
  885. LLRect panel_rect = panel->getRect();
  886. panel_rect.setLeftTopAndSize( left, top, width, height);
  887. panel->reshape( width, height, 1);
  888. panel->setRect(panel_rect);
  889. }
  890. BOOL LLAccordionCtrlTab::handleToolTip(S32 x, S32 y, MASK mask)
  891. {
  892. //header may be not the first child but we need to process it first
  893. if(y >= (getRect().getHeight() - HEADER_HEIGHT - HEADER_HEIGHT/2) )
  894. {
  895. //inside tab header
  896. //fix for EXT-6619
  897. mHeader->handleToolTip(x, y, mask);
  898. return TRUE;
  899. }
  900. return LLUICtrl::handleToolTip(x, y, mask);
  901. }
  902. BOOL LLAccordionCtrlTab::handleScrollWheel ( S32 x, S32 y, S32 clicks )
  903. {
  904. if( LLUICtrl::handleScrollWheel(x,y,clicks))
  905. {
  906. return TRUE;
  907. }
  908. if( mScrollbar && mScrollbar->getVisible() && mScrollbar->handleScrollWheel( 0, 0, clicks ) )
  909. {
  910. return TRUE;
  911. }
  912. return FALSE;
  913. }