/** * @file llaccordionctrl.cpp * @brief Accordion panel implementation * * $LicenseInfo:firstyear=2009&license=viewerlgpl$ * Second Life Viewer Source Code * Copyright (C) 2010, Linden Research, Inc. * * This library is free software; you can redistribute it and/or * modify it under the terms of the GNU Lesser General Public * License as published by the Free Software Foundation; * version 2.1 of the License only. * * This library is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU * Lesser General Public License for more details. * * You should have received a copy of the GNU Lesser General Public * License along with this library; if not, write to the Free Software * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA * * Linden Research, Inc., 945 Battery Street, San Francisco, CA 94111 USA * $/LicenseInfo$ */ #include "linden_common.h" #include "llaccordionctrl.h" #include "llaccordionctrltab.h" #include "lluictrlfactory.h" // builds floaters from XML #include "llwindow.h" #include "llfocusmgr.h" #include "lllocalcliprect.h" #include "boost/bind.hpp" static const S32 BORDER_MARGIN = 2; static const S32 PARENT_BORDER_MARGIN = 5; static const S32 VERTICAL_MULTIPLE = 16; static const F32 MIN_AUTO_SCROLL_RATE = 120.f; static const F32 MAX_AUTO_SCROLL_RATE = 500.f; static const F32 AUTO_SCROLL_RATE_ACCEL = 120.f; // LLAccordionCtrl =================================================================| static LLDefaultChildRegistry::Register t2("accordion"); LLAccordionCtrl::LLAccordionCtrl(const Params& params):LLPanel(params) , mFitParent(params.fit_parent) , mAutoScrolling( false ) , mAutoScrollRate( 0.f ) , mSelectedTab( NULL ) , mTabComparator( NULL ) , mNoVisibleTabsHelpText(NULL) , mNoVisibleTabsOrigString(params.no_visible_tabs_text.initial_value().asString()) , mSkipScrollToChild(false) { initNoTabsWidget(params.no_matched_tabs_text); mSingleExpansion = params.single_expansion; if (mFitParent && !mSingleExpansion) { LL_INFOS() << "fit_parent works best when combined with single_expansion" << LL_ENDL; } } LLAccordionCtrl::LLAccordionCtrl() : LLPanel() , mAutoScrolling( false ) , mAutoScrollRate( 0.f ) , mSelectedTab( NULL ) , mNoVisibleTabsHelpText(NULL) { initNoTabsWidget(LLTextBox::Params()); mSingleExpansion = false; mFitParent = false; buildFromFile( "accordion_parent.xml"); } //--------------------------------------------------------------------------------- void LLAccordionCtrl::draw() { if (mAutoScrolling) { // add acceleration to autoscroll mAutoScrollRate = llmin(mAutoScrollRate + (LLFrameTimer::getFrameDeltaTimeF32() * AUTO_SCROLL_RATE_ACCEL), MAX_AUTO_SCROLL_RATE); } else { // reset to minimum for next time mAutoScrollRate = MIN_AUTO_SCROLL_RATE; } // clear this flag to be set on next call to autoScroll mAutoScrolling = false; LLRect local_rect(0, getRect().getHeight(), getRect().getWidth(), 0); LLLocalClipRect clip(local_rect); LLPanel::draw(); } //--------------------------------------------------------------------------------- bool LLAccordionCtrl::postBuild() { static LLUICachedControl scrollbar_size("UIScrollbarSize", 0); LLRect scroll_rect; scroll_rect.setOriginAndSize( getRect().getWidth() - scrollbar_size, 1, scrollbar_size, getRect().getHeight() - 1); LLScrollbar::Params sbparams; sbparams.name("scrollable vertical"); sbparams.rect(scroll_rect); sbparams.orientation(LLScrollbar::VERTICAL); sbparams.doc_size(mInnerRect.getHeight()); sbparams.doc_pos(0); sbparams.page_size(mInnerRect.getHeight()); sbparams.step_size(VERTICAL_MULTIPLE); sbparams.follows.flags(FOLLOWS_RIGHT | FOLLOWS_TOP | FOLLOWS_BOTTOM); sbparams.change_callback(boost::bind(&LLAccordionCtrl::onScrollPosChangeCallback, this, _1, _2)); mScrollbar = LLUICtrlFactory::create(sbparams); LLView::addChild(mScrollbar); mScrollbar->setVisible(false); mScrollbar->setFollowsRight(); mScrollbar->setFollowsTop(); mScrollbar->setFollowsBottom(); //if it was created from xml... std::vector accordion_tabs; for (child_list_const_iter_t it = getChildList()->begin(); getChildList()->end() != it; ++it) { LLAccordionCtrlTab* accordion_tab = dynamic_cast(*it); if (accordion_tab == NULL) continue; if (std::find(mAccordionTabs.begin(), mAccordionTabs.end(), accordion_tab) == mAccordionTabs.end()) { accordion_tabs.push_back(accordion_tab); } } for (std::vector::reverse_iterator it = accordion_tabs.rbegin(); it < accordion_tabs.rend(); ++it) { addCollapsibleCtrl(*it); } arrange(); if (mSingleExpansion) { if (!mAccordionTabs[0]->getDisplayChildren()) mAccordionTabs[0]->setDisplayChildren(true); for (size_t i = 1; i < mAccordionTabs.size(); ++i) { if (mAccordionTabs[i]->getDisplayChildren()) mAccordionTabs[i]->setDisplayChildren(false); } } updateNoTabsHelpTextVisibility(); return true; } //--------------------------------------------------------------------------------- LLAccordionCtrl::~LLAccordionCtrl() { mAccordionTabs.clear(); } //--------------------------------------------------------------------------------- void LLAccordionCtrl::reshape(S32 width, S32 height, bool called_from_parent) { // adjust our rectangle LLRect rcLocal = getRect(); rcLocal.mRight = rcLocal.mLeft + width; rcLocal.mTop = rcLocal.mBottom + height; // get textbox a chance to reshape its content mNoVisibleTabsHelpText->reshape(width, height, called_from_parent); setRect(rcLocal); // assume that help text is always fit accordion. // necessary text paddings can be set via h_pad and v_pad mNoVisibleTabsHelpText->setRect(getLocalRect()); arrange(); } //--------------------------------------------------------------------------------- bool LLAccordionCtrl::handleRightMouseDown(S32 x, S32 y, MASK mask) { return LLPanel::handleRightMouseDown(x, y, mask); } //--------------------------------------------------------------------------------- void LLAccordionCtrl::shiftAccordionTabs(S16 panel_num, S32 delta) { for (size_t i = panel_num; i < mAccordionTabs.size(); ++i) { ctrlShiftVertical(mAccordionTabs[i],delta); } } //--------------------------------------------------------------------------------- void LLAccordionCtrl::onCollapseCtrlCloseOpen(S16 panel_num) { if (mSingleExpansion) { for (size_t i = 0; i < mAccordionTabs.size(); ++i) { if (i == panel_num) continue; if (mAccordionTabs[i]->getDisplayChildren()) mAccordionTabs[i]->setDisplayChildren(false); } } arrange(); } void LLAccordionCtrl::show_hide_scrollbar(S32 width, S32 height) { calcRecuiredHeight(); if (getRecuiredHeight() > height) showScrollbar(width, height); else hideScrollbar(width, height); } void LLAccordionCtrl::showScrollbar(S32 width, S32 height) { bool was_visible = mScrollbar->getVisible(); mScrollbar->setVisible(true); static LLUICachedControl scrollbar_size ("UIScrollbarSize", 0); ctrlSetLeftTopAndSize(mScrollbar , width - scrollbar_size - PARENT_BORDER_MARGIN / 2 , height - PARENT_BORDER_MARGIN , scrollbar_size , height - PARENT_BORDER_MARGIN * 2); mScrollbar->setPageSize(height); mScrollbar->setDocParams(mInnerRect.getHeight(), mScrollbar->getDocPos()); if (was_visible) { S32 scroll_pos = llmin(mScrollbar->getDocPos(), getRecuiredHeight() - height - 1); mScrollbar->setDocPos(scroll_pos); } } void LLAccordionCtrl::hideScrollbar(S32 width, S32 height) { if (mScrollbar->getVisible() == false) return; mScrollbar->setVisible(false); static LLUICachedControl scrollbar_size ("UIScrollbarSize", 0); S32 panel_width = width - 2*BORDER_MARGIN; // Reshape all accordions and shift all draggers for (size_t i = 0; i < mAccordionTabs.size(); ++i) { LLRect panel_rect = mAccordionTabs[i]->getRect(); ctrlSetLeftTopAndSize(mAccordionTabs[i], panel_rect.mLeft, panel_rect.mTop, panel_width, panel_rect.getHeight()); } mScrollbar->setDocPos(0); if (!mAccordionTabs.empty()) { S32 panel_top = height - BORDER_MARGIN; // Top coordinate of the first panel S32 diff = panel_top - mAccordionTabs[0]->getRect().mTop; shiftAccordionTabs(0, diff); } } //--------------------------------------------------------------------------------- S32 LLAccordionCtrl::calcRecuiredHeight() { S32 rec_height = 0; std::vector::iterator panel; for(panel=mAccordionTabs.begin(); panel!=mAccordionTabs.end(); ++panel) { LLAccordionCtrlTab* accordion_tab = dynamic_cast(*panel); if(accordion_tab && accordion_tab->getVisible()) { rec_height += accordion_tab->getRect().getHeight(); } } mInnerRect.setLeftTopAndSize(0, rec_height + BORDER_MARGIN * 2, getRect().getWidth(), rec_height + BORDER_MARGIN); return mInnerRect.getHeight(); } //--------------------------------------------------------------------------------- void LLAccordionCtrl::ctrlSetLeftTopAndSize(LLView* panel, S32 left, S32 top, S32 width, S32 height) { if (!panel) return; LLRect panel_rect = panel->getRect(); panel_rect.setLeftTopAndSize( left, top, width, height); panel->reshape( width, height, 1); panel->setRect(panel_rect); } void LLAccordionCtrl::ctrlShiftVertical(LLView* panel, S32 delta) { if (!panel) return; panel->translate(0,delta); } //--------------------------------------------------------------------------------- void LLAccordionCtrl::addCollapsibleCtrl(LLView* view) { LLAccordionCtrlTab* accordion_tab = dynamic_cast(view); if (!accordion_tab) return; if (std::find(beginChild(), endChild(), accordion_tab) == endChild()) addChild(accordion_tab); mAccordionTabs.push_back(accordion_tab); accordion_tab->setDropDownStateChangedCallback( boost::bind(&LLAccordionCtrl::onCollapseCtrlCloseOpen, this, (S16)(mAccordionTabs.size() - 1)) ); arrange(); } void LLAccordionCtrl::removeCollapsibleCtrl(LLView* view) { LLAccordionCtrlTab* accordion_tab = dynamic_cast(view); if(!accordion_tab) return; if(std::find(beginChild(), endChild(), accordion_tab) != endChild()) removeChild(accordion_tab); for (std::vector::iterator iter = mAccordionTabs.begin(); iter != mAccordionTabs.end(); ++iter) { if (accordion_tab == (*iter)) { mAccordionTabs.erase(iter); break; } } // if removed is selected - reset selection if (mSelectedTab == view) { mSelectedTab = NULL; } } void LLAccordionCtrl::initNoTabsWidget(const LLTextBox::Params& tb_params) { LLTextBox::Params tp = tb_params; tp.rect(getLocalRect()); mNoMatchedTabsOrigString = tp.initial_value().asString(); mNoVisibleTabsHelpText = LLUICtrlFactory::create(tp, this); } void LLAccordionCtrl::updateNoTabsHelpTextVisibility() { bool visible_exists = false; std::vector::const_iterator it = mAccordionTabs.begin(); const std::vector::const_iterator it_end = mAccordionTabs.end(); while (it < it_end) { if ((*(it++))->getVisible()) { visible_exists = true; break; } } mNoVisibleTabsHelpText->setVisible(visible_exists ? false : true); } void LLAccordionCtrl::arrangeSingle() { S32 panel_left = BORDER_MARGIN; // Margin from left side of Splitter S32 panel_top = getRect().getHeight() - BORDER_MARGIN; // Top coordinate of the first panel S32 panel_width = getRect().getWidth() - 4; S32 panel_height; S32 collapsed_height = 0; for (size_t i = 0; i < mAccordionTabs.size(); ++i) { LLAccordionCtrlTab* accordion_tab = dynamic_cast(mAccordionTabs[i]); if (accordion_tab->getVisible() == false) // Skip hidden accordion tabs continue; if (!accordion_tab->isExpanded() ) { collapsed_height+=mAccordionTabs[i]->getRect().getHeight(); } } S32 expanded_height = getRect().getHeight() - BORDER_MARGIN - collapsed_height; for (size_t i = 0; i < mAccordionTabs.size(); ++i) { LLAccordionCtrlTab* accordion_tab = dynamic_cast(mAccordionTabs[i]); if (accordion_tab->getVisible() == false) // Skip hidden accordion tabs continue; if (!accordion_tab->isExpanded() ) { panel_height = accordion_tab->getRect().getHeight(); } else { if (mFitParent) { panel_height = expanded_height; } else { if (accordion_tab->getAccordionView()) { panel_height = accordion_tab->getAccordionView()->getRect().getHeight() + accordion_tab->getHeaderHeight() + BORDER_MARGIN * 2; } else { panel_height = accordion_tab->getRect().getHeight(); } } } // make sure at least header is shown panel_height = llmax(panel_height, accordion_tab->getHeaderHeight()); ctrlSetLeftTopAndSize(mAccordionTabs[i], panel_left, panel_top, panel_width, panel_height); panel_top -= mAccordionTabs[i]->getRect().getHeight(); } show_hide_scrollbar(getRect().getWidth(), getRect().getHeight()); updateLayout(getRect().getWidth(), getRect().getHeight()); } void LLAccordionCtrl::arrangeMultiple() { S32 panel_left = BORDER_MARGIN; // Margin from left side of Splitter S32 panel_top = getRect().getHeight() - BORDER_MARGIN; // Top coordinate of the first panel S32 panel_width = getRect().getWidth() - 4; //Calculate params for (size_t i = 0; i < mAccordionTabs.size(); i++ ) { LLAccordionCtrlTab* accordion_tab = dynamic_cast(mAccordionTabs[i]); if (accordion_tab->getVisible() == false) // Skip hidden accordion tabs continue; if (!accordion_tab->isExpanded() ) { ctrlSetLeftTopAndSize(mAccordionTabs[i], panel_left, panel_top, panel_width, accordion_tab->getRect().getHeight()); panel_top -= mAccordionTabs[i]->getRect().getHeight(); } else { S32 panel_height = accordion_tab->getRect().getHeight(); if (mFitParent) { // All expanded tabs will have equal height panel_height = calcExpandedTabHeight(i, panel_top); ctrlSetLeftTopAndSize(accordion_tab, panel_left, panel_top, panel_width, panel_height); // Try to make accordion tab fit accordion view height. // Accordion View should implement getRequiredRect() and provide valid height S32 optimal_height = accordion_tab->getAccordionView()->getRequiredRect().getHeight(); optimal_height += accordion_tab->getHeaderHeight() + 2 * BORDER_MARGIN; if (optimal_height < panel_height) { panel_height = optimal_height; } // minimum tab height is equal to header height if (mAccordionTabs[i]->getHeaderHeight() > panel_height) { panel_height = mAccordionTabs[i]->getHeaderHeight(); } } ctrlSetLeftTopAndSize(mAccordionTabs[i], panel_left, panel_top, panel_width, panel_height); panel_top -= panel_height; } } show_hide_scrollbar(getRect().getWidth(), getRect().getHeight()); updateLayout(getRect().getWidth(), getRect().getHeight()); } void LLAccordionCtrl::arrange() { updateNoTabsHelpTextVisibility(); if (mAccordionTabs.empty()) { // Nothing to arrange return; } if (mAccordionTabs.size() == 1) { S32 panel_top = getRect().getHeight() - BORDER_MARGIN; // Top coordinate of the first panel S32 panel_width = getRect().getWidth() - 4; LLAccordionCtrlTab* accordion_tab = dynamic_cast(mAccordionTabs[0]); LLRect panel_rect = accordion_tab->getRect(); S32 panel_height = getRect().getHeight() - BORDER_MARGIN * 2; if (accordion_tab->getFitParent()) panel_height = accordion_tab->getRect().getHeight(); ctrlSetLeftTopAndSize(accordion_tab, panel_rect.mLeft, panel_top, panel_width, panel_height); show_hide_scrollbar(getRect().getWidth(), getRect().getHeight()); return; } if (mSingleExpansion) arrangeSingle(); else arrangeMultiple(); } //--------------------------------------------------------------------------------- bool LLAccordionCtrl::handleScrollWheel(S32 x, S32 y, S32 clicks) { if (LLPanel::handleScrollWheel(x, y, clicks)) return true; if (mScrollbar->getVisible() && mScrollbar->handleScrollWheel(0, 0, clicks)) return true; return false; } bool LLAccordionCtrl::handleKeyHere(KEY key, MASK mask) { if (mScrollbar->getVisible() && mScrollbar->handleKeyHere(key, mask)) return true; return LLPanel::handleKeyHere(key, mask); } bool LLAccordionCtrl::handleDragAndDrop(S32 x, S32 y, MASK mask, bool drop, EDragAndDropType cargo_type, void* cargo_data, EAcceptance* accept, std::string& tooltip_msg) { // Scroll folder view if needed. Never accepts a drag or drop. *accept = ACCEPT_NO; bool handled = autoScroll(x, y); if (!handled) { handled = childrenHandleDragAndDrop(x, y, mask, drop, cargo_type, cargo_data, accept, tooltip_msg) != NULL; } return true; } bool LLAccordionCtrl::autoScroll(S32 x, S32 y) { static LLUICachedControl scrollbar_size ("UIScrollbarSize", 0); bool scrolling = false; if (mScrollbar->getVisible()) { LLRect rect_local(0, getRect().getHeight(), getRect().getWidth() - scrollbar_size, 0); LLRect screen_local_extents; // clip rect against root view screenRectToLocal(getRootView()->getLocalRect(), &screen_local_extents); rect_local.intersectWith(screen_local_extents); // autoscroll region should take up no more than one third of visible scroller area S32 auto_scroll_region_height = llmin(rect_local.getHeight() / 3, 10); S32 auto_scroll_speed = ll_round(mAutoScrollRate * LLFrameTimer::getFrameDeltaTimeF32()); LLRect bottom_scroll_rect = screen_local_extents; bottom_scroll_rect.mTop = rect_local.mBottom + auto_scroll_region_height; if (bottom_scroll_rect.pointInRect( x, y ) && (mScrollbar->getDocPos() < mScrollbar->getDocPosMax())) { mScrollbar->setDocPos(mScrollbar->getDocPos() + auto_scroll_speed); mAutoScrolling = true; scrolling = true; } LLRect top_scroll_rect = screen_local_extents; top_scroll_rect.mBottom = rect_local.mTop - auto_scroll_region_height; if (top_scroll_rect.pointInRect(x, y) && (mScrollbar->getDocPos() > 0)) { mScrollbar->setDocPos(mScrollbar->getDocPos() - auto_scroll_speed); mAutoScrolling = true; scrolling = true; } } return scrolling ? true : false; } void LLAccordionCtrl::updateLayout(S32 width, S32 height) { S32 panel_top = height - BORDER_MARGIN ; if (mScrollbar->getVisible()) panel_top += mScrollbar->getDocPos(); S32 panel_width = width - BORDER_MARGIN * 2; static LLUICachedControl scrollbar_size ("UIScrollbarSize", 0); if (mScrollbar->getVisible()) panel_width -= scrollbar_size; // set sizes for first panels and dragbars for (size_t i = 0; i < mAccordionTabs.size(); ++i) { if (!mAccordionTabs[i]->getVisible()) continue; LLRect panel_rect = mAccordionTabs[i]->getRect(); ctrlSetLeftTopAndSize(mAccordionTabs[i], panel_rect.mLeft, panel_top, panel_width, panel_rect.getHeight()); panel_top -= panel_rect.getHeight(); } } void LLAccordionCtrl::onScrollPosChangeCallback(S32, LLScrollbar*) { updateLayout(getRect().getWidth(), getRect().getHeight()); } // virtual void LLAccordionCtrl::onUpdateScrollToChild(const LLUICtrl *cntrl) { if (mScrollbar && mScrollbar->getVisible() && !mSkipScrollToChild) { // same as scrollToShowRect LLRect rect; cntrl->localRectToOtherView(cntrl->getLocalRect(), &rect, this); // Translate to parent coordinatess to check if we are in visible rectangle rect.translate(getRect().mLeft, getRect().mBottom); if (!getRect().contains(rect)) { // for accordition's scroll, height is in pixels // Back to local coords and calculate position for scroller S32 bottom = mScrollbar->getDocPos() - rect.mBottom + getRect().mBottom; S32 top = mScrollbar->getDocPos() - rect.mTop + getRect().mTop; S32 scroll_pos = llclamp(mScrollbar->getDocPos(), bottom, // min vertical scroll top); // max vertical scroll mScrollbar->setDocPos(scroll_pos); } } LLUICtrl::onUpdateScrollToChild(cntrl); } void LLAccordionCtrl::onOpen(const LLSD& key) { for (size_t i = 0; i < mAccordionTabs.size(); ++i) { LLAccordionCtrlTab* accordion_tab = dynamic_cast(mAccordionTabs[i]); LLPanel* panel = dynamic_cast(accordion_tab->getAccordionView()); if (panel != NULL) { panel->onOpen(key); } } } S32 LLAccordionCtrl::notifyParent(const LLSD& info) { if (info.has("action")) { std::string str_action = info["action"]; if (str_action == "size_changes") { // arrange(); return 1; } if (str_action == "select_next") { for (size_t i = 0; i < mAccordionTabs.size(); ++i) { LLAccordionCtrlTab* accordion_tab = dynamic_cast(mAccordionTabs[i]); if (accordion_tab->hasFocus()) { while (++i < mAccordionTabs.size()) { if (mAccordionTabs[i]->getVisible()) break; } if (i < mAccordionTabs.size()) { accordion_tab = dynamic_cast(mAccordionTabs[i]); accordion_tab->notify(LLSD().with("action","select_first")); return 1; } break; } } return 0; } if (str_action == "select_prev") { for (size_t i = 0; i < mAccordionTabs.size(); ++i) { LLAccordionCtrlTab* accordion_tab = dynamic_cast(mAccordionTabs[i]); if (accordion_tab->hasFocus() && i > 0) { bool prev_visible_tab_found = false; while (i > 0) { if (mAccordionTabs[--i]->getVisible()) { prev_visible_tab_found = true; break; } } if (prev_visible_tab_found) { accordion_tab = dynamic_cast(mAccordionTabs[i]); accordion_tab->notify(LLSD().with("action","select_last")); return 1; } break; } } return 0; } if (str_action == "select_current") { for (size_t i = 0; i < mAccordionTabs.size(); ++i) { // Set selection to the currently focused tab. if (mAccordionTabs[i]->hasFocus()) { if (mAccordionTabs[i] != mSelectedTab) { if (mSelectedTab) { mSelectedTab->setSelected(false); } mSelectedTab = mAccordionTabs[i]; mSelectedTab->setSelected(true); } return 1; } } return 0; } if (str_action == "deselect_current") { // Reset selection to the currently selected tab. if (mSelectedTab) { mSelectedTab->setSelected(false); mSelectedTab = NULL; return 1; } return 0; } } else if (info.has("scrollToShowRect")) { LLRect screen_rc, local_rc; screen_rc.setValue(info["scrollToShowRect"]); screenRectToLocal(screen_rc, &local_rc); // Translate to parent coordinatess to check if we are in visible rectangle local_rc.translate(getRect().mLeft, getRect().mBottom); if (!getRect().contains (local_rc)) { // Back to local coords and calculate position for scroller S32 bottom = mScrollbar->getDocPos() - local_rc.mBottom + getRect().mBottom; S32 top = mScrollbar->getDocPos() - local_rc.mTop + getRect().mTop; S32 scroll_pos = llclamp(mScrollbar->getDocPos(), bottom, // min vertical scroll top); // max vertical scroll mScrollbar->setDocPos(scroll_pos); } return 1; } else if (info.has("child_visibility_change")) { bool new_visibility = info["child_visibility_change"]; if (new_visibility) { // there is at least one visible tab mNoVisibleTabsHelpText->setVisible(false); } else { // it could be the latest visible tab, check all of them updateNoTabsHelpTextVisibility(); } } return LLPanel::notifyParent(info); } void LLAccordionCtrl::reset() { if (mScrollbar) mScrollbar->setDocPos(0); } void LLAccordionCtrl::expandDefaultTab() { if (!mAccordionTabs.empty()) { LLAccordionCtrlTab* tab = mAccordionTabs.front(); if (!tab->getDisplayChildren()) { tab->setDisplayChildren(true); } for (size_t i = 1; i < mAccordionTabs.size(); ++i) { tab = mAccordionTabs[i]; if (tab->getDisplayChildren()) { tab->setDisplayChildren(false); } } arrange(); } } void LLAccordionCtrl::sort() { if (!mTabComparator) { LL_WARNS() << "No comparator specified for sorting accordion tabs." << LL_ENDL; return; } std::sort(mAccordionTabs.begin(), mAccordionTabs.end(), LLComparatorAdaptor(*mTabComparator)); arrange(); } void LLAccordionCtrl::setFilterSubString(const std::string& filter_string) { LLStringUtil::format_map_t args; args["[SEARCH_TERM]"] = LLURI::escape(filter_string); std::string text = filter_string.empty() ? mNoVisibleTabsOrigString : mNoMatchedTabsOrigString; LLStringUtil::format(text, args); mNoVisibleTabsHelpText->setValue(text); } const LLAccordionCtrlTab* LLAccordionCtrl::getExpandedTab() const { typedef std::vector::const_iterator tabs_const_iterator; const LLAccordionCtrlTab* result = 0; for (tabs_const_iterator i = mAccordionTabs.begin(); i != mAccordionTabs.end(); ++i) { if ((*i)->isExpanded()) { result = *i; break; } } return result; } S32 LLAccordionCtrl::calcExpandedTabHeight(S32 tab_index /* = 0 */, S32 available_height /* = 0 */) { if (tab_index < 0) { return available_height; } S32 collapsed_tabs_height = 0; S32 num_expanded = 0; for (size_t n = tab_index; n < mAccordionTabs.size(); ++n) { if (!mAccordionTabs[n]->isExpanded()) { collapsed_tabs_height += mAccordionTabs[n]->getHeaderHeight(); } else { ++num_expanded; } } if (0 == num_expanded) { return available_height; } S32 expanded_tab_height = available_height - collapsed_tabs_height - BORDER_MARGIN; // top BORDER_MARGIN is added in arrange(), here we add bottom BORDER_MARGIN expanded_tab_height /= num_expanded; return expanded_tab_height; }