summaryrefslogtreecommitdiff
path: root/indra/llui/lltabcontainer.cpp
diff options
context:
space:
mode:
Diffstat (limited to 'indra/llui/lltabcontainer.cpp')
-rw-r--r--indra/llui/lltabcontainer.cpp1469
1 files changed, 1469 insertions, 0 deletions
diff --git a/indra/llui/lltabcontainer.cpp b/indra/llui/lltabcontainer.cpp
new file mode 100644
index 0000000000..fd85dbb2f4
--- /dev/null
+++ b/indra/llui/lltabcontainer.cpp
@@ -0,0 +1,1469 @@
+/**
+ * @file lltabcontainer.cpp
+ * @brief LLTabContainerCommon base class
+ *
+ * Copyright (c) 2001-$CurrentYear$, Linden Research, Inc.
+ * $License$
+ */
+
+#include "linden_common.h"
+
+#include "lltabcontainer.h"
+
+#include "llfocusmgr.h"
+#include "llfontgl.h"
+#include "llgl.h"
+
+#include "llbutton.h"
+#include "llrect.h"
+#include "llpanel.h"
+#include "llresmgr.h"
+#include "llkeyboard.h"
+#include "llresizehandle.h"
+#include "llui.h"
+#include "lltextbox.h"
+#include "llcontrol.h"
+#include "llcriticaldamp.h"
+#include "lluictrlfactory.h"
+
+#include "lltabcontainervertical.h"
+
+#include "llglheaders.h"
+
+const F32 SCROLL_STEP_TIME = 0.4f;
+const S32 TAB_PADDING = 15;
+const S32 TABCNTR_TAB_MIN_WIDTH = 60;
+const S32 TABCNTR_TAB_MAX_WIDTH = 150;
+const S32 TABCNTR_TAB_PARTIAL_WIDTH = 12; // When tabs are parially obscured, how much can you still see.
+const S32 TABCNTR_TAB_HEIGHT = 16;
+const S32 TABCNTR_ARROW_BTN_SIZE = 16;
+const S32 TABCNTR_BUTTON_PANEL_OVERLAP = 1; // how many pixels the tab buttons and tab panels overlap.
+const S32 TABCNTR_TAB_H_PAD = 4;
+
+
+LLTabContainerCommon::LLTabContainerCommon(
+ const LLString& name, const LLRect& rect,
+ TabPosition pos,
+ void(*close_callback)(void*), void* callback_userdata,
+ BOOL bordered )
+ :
+ LLPanel(name, rect, bordered),
+ mCurrentTabIdx(-1),
+ mScrolled(FALSE),
+ mScrollPos(0),
+ mScrollPosPixels(0),
+ mMaxScrollPos(0),
+ mCloseCallback( close_callback ),
+ mCallbackUserdata( callback_userdata ),
+ mTitleBox(NULL),
+ mTopBorderHeight(LLPANEL_BORDER_WIDTH),
+ mTabPosition(pos)
+{
+ setMouseOpaque(FALSE);
+}
+
+
+LLTabContainerCommon::LLTabContainerCommon(
+ const LLString& name,
+ const LLString& rect_control,
+ TabPosition pos,
+ void(*close_callback)(void*), void* callback_userdata,
+ BOOL bordered )
+ :
+ LLPanel(name, rect_control, bordered),
+ mCurrentTabIdx(-1),
+ mScrolled(FALSE),
+ mScrollPos(0),
+ mScrollPosPixels(0),
+ mMaxScrollPos(0),
+ mCloseCallback( close_callback ),
+ mCallbackUserdata( callback_userdata ),
+ mTitleBox(NULL),
+ mTopBorderHeight(LLPANEL_BORDER_WIDTH),
+ mTabPosition(pos)
+{
+ setMouseOpaque(FALSE);
+}
+
+
+LLTabContainerCommon::~LLTabContainerCommon()
+{
+ std::for_each(mTabList.begin(), mTabList.end(), DeletePointer());
+}
+
+LLView* LLTabContainerCommon::getChildByName(const LLString& name, BOOL recurse) const
+{
+ tuple_list_t::const_iterator itor;
+ for (itor = mTabList.begin(); itor != mTabList.end(); ++itor)
+ {
+ LLPanel *panel = (*itor)->mTabPanel;
+ if (panel->getName() == name)
+ {
+ return panel;
+ }
+ }
+ if (recurse)
+ {
+ for (itor = mTabList.begin(); itor != mTabList.end(); ++itor)
+ {
+ LLPanel *panel = (*itor)->mTabPanel;
+ LLView *child = panel->getChildByName(name, recurse);
+ if (child)
+ {
+ return child;
+ }
+ }
+ }
+ return LLView::getChildByName(name, recurse);
+}
+
+void LLTabContainerCommon::addPlaceholder(LLPanel* child, const LLString& label)
+{
+ addTabPanel(child, label, FALSE, NULL, NULL, 0, TRUE);
+}
+
+void LLTabContainerCommon::removeTabPanel(LLPanel* child)
+{
+ BOOL has_focus = gFocusMgr.childHasKeyboardFocus(this);
+
+ // If the tab being deleted is the selected one, select a different tab.
+ for(std::vector<LLTabTuple*>::iterator iter = mTabList.begin(); iter != mTabList.end(); ++iter)
+ {
+ LLTabTuple* tuple = *iter;
+ if( tuple->mTabPanel == child )
+ {
+ removeChild( tuple->mButton );
+ delete tuple->mButton;
+
+ removeChild( tuple->mTabPanel );
+// delete tuple->mTabPanel;
+
+ mTabList.erase( iter );
+ delete tuple;
+
+ break;
+ }
+ }
+ if (mCurrentTabIdx >= (S32)mTabList.size())
+ {
+ mCurrentTabIdx = mTabList.size()-1;
+ }
+ selectTab(mCurrentTabIdx);
+ if (has_focus)
+ {
+ LLPanel* panelp = getPanelByIndex(mCurrentTabIdx);
+ if (panelp)
+ {
+ panelp->setFocus(TRUE);
+ }
+ }
+
+ updateMaxScrollPos();
+}
+
+void LLTabContainerCommon::deleteAllTabs()
+{
+ // Remove all the tab buttons and delete them. Also, unlink all the child panels.
+ for(std::vector<LLTabTuple*>::iterator iter = mTabList.begin(); iter != mTabList.end(); ++iter)
+ {
+ LLTabTuple* tuple = *iter;
+
+ removeChild( tuple->mButton );
+ delete tuple->mButton;
+
+ removeChild( tuple->mTabPanel );
+// delete tuple->mTabPanel;
+ }
+
+ // Actually delete the tuples themselves
+ std::for_each(mTabList.begin(), mTabList.end(), DeletePointer());
+ mTabList.clear();
+
+ // And there isn't a current tab any more
+ mCurrentTabIdx = -1;
+}
+
+
+LLPanel* LLTabContainerCommon::getCurrentPanel()
+{
+ if (mCurrentTabIdx < 0 || mCurrentTabIdx >= (S32) mTabList.size()) return NULL;
+
+ return mTabList[mCurrentTabIdx]->mTabPanel;
+}
+
+LLTabContainerCommon::LLTabTuple* LLTabContainerCommon::getTabByPanel(LLPanel* child)
+{
+ for(tuple_list_t::iterator iter = mTabList.begin(); iter != mTabList.end(); ++iter)
+ {
+ LLTabTuple* tuple = *iter;
+ if( tuple->mTabPanel == child )
+ {
+ return tuple;
+ }
+ }
+ return NULL;
+}
+
+void LLTabContainerCommon::setTabChangeCallback(LLPanel* tab, void (*on_tab_clicked)(void*, bool))
+{
+ LLTabTuple* tuplep = getTabByPanel(tab);
+ if (tuplep)
+ {
+ tuplep->mOnChangeCallback = on_tab_clicked;
+ }
+}
+
+void LLTabContainerCommon::setTabUserData(LLPanel* tab, void* userdata)
+{
+ LLTabTuple* tuplep = getTabByPanel(tab);
+ if (tuplep)
+ {
+ tuplep->mUserData = userdata;
+ }
+}
+
+S32 LLTabContainerCommon::getCurrentPanelIndex()
+{
+ return mCurrentTabIdx;
+}
+
+S32 LLTabContainerCommon::getTabCount()
+{
+ return mTabList.size();
+}
+
+LLPanel* LLTabContainerCommon::getPanelByIndex(S32 index)
+{
+ if (index >= 0 && index < (S32)mTabList.size())
+ {
+ return mTabList[index]->mTabPanel;
+ }
+ return NULL;
+}
+
+S32 LLTabContainerCommon::getIndexForPanel(LLPanel* panel)
+{
+ for (S32 index = 0; index < (S32)mTabList.size(); index++)
+ {
+ if (mTabList[index]->mTabPanel == panel)
+ {
+ return index;
+ }
+ }
+ return -1;
+}
+
+S32 LLTabContainerCommon::getPanelIndexByTitle(const LLString& title)
+{
+ for (S32 index = 0 ; index < (S32)mTabList.size(); index++)
+ {
+ if (title == mTabList[index]->mButton->getLabelSelected())
+ {
+ return index;
+ }
+ }
+ return -1;
+}
+
+LLPanel *LLTabContainerCommon::getPanelByName(const LLString& name)
+{
+ for (S32 index = 0 ; index < (S32)mTabList.size(); index++)
+ {
+ LLPanel *panel = mTabList[index]->mTabPanel;
+ if (name == panel->getName())
+ {
+ return panel;
+ }
+ }
+ return NULL;
+}
+
+
+void LLTabContainerCommon::scrollNext()
+{
+ // No wrap
+ if( mScrollPos < mMaxScrollPos )
+ {
+ mScrollPos++;
+ }
+}
+
+void LLTabContainerCommon::scrollPrev()
+{
+ // No wrap
+ if( mScrollPos > 0 )
+ {
+ mScrollPos--;
+ }
+}
+
+void LLTabContainerCommon::enableTabButton(S32 which, BOOL enable)
+{
+ if (which >= 0 && which < (S32)mTabList.size())
+ {
+ mTabList[which]->mButton->setEnabled(enable);
+ }
+}
+
+BOOL LLTabContainerCommon::selectTabPanel(LLPanel* child)
+{
+ S32 idx = 0;
+ for(tuple_list_t::iterator iter = mTabList.begin(); iter != mTabList.end(); ++iter)
+ {
+ LLTabTuple* tuple = *iter;
+ if( tuple->mTabPanel == child )
+ {
+ return selectTab( idx );
+ }
+ idx++;
+ }
+ return FALSE;
+}
+
+BOOL LLTabContainerCommon::selectTabByName(const LLString& name)
+{
+ LLPanel* panel = getPanelByName(name);
+ if (!panel)
+ {
+ llwarns << "LLTabContainerCommon::selectTabByName("
+ << name << ") failed" << llendl;
+ return FALSE;
+ }
+
+ BOOL result = selectTabPanel(panel);
+ return result;
+}
+
+
+void LLTabContainerCommon::selectFirstTab()
+{
+ selectTab( 0 );
+}
+
+
+void LLTabContainerCommon::selectLastTab()
+{
+ selectTab( mTabList.size()-1 );
+}
+
+
+void LLTabContainerCommon::selectNextTab()
+{
+ BOOL tab_has_focus = FALSE;
+ if (mCurrentTabIdx >= 0 && mTabList[mCurrentTabIdx]->mButton->hasFocus())
+ {
+ tab_has_focus = TRUE;
+ }
+ S32 idx = mCurrentTabIdx+1;
+ if (idx >= (S32)mTabList.size())
+ idx = 0;
+ while (!selectTab(idx) && idx != mCurrentTabIdx)
+ {
+ idx = (idx + 1 ) % (S32)mTabList.size();
+ }
+
+ if (tab_has_focus)
+ {
+ mTabList[idx]->mButton->setFocus(TRUE);
+ }
+}
+
+void LLTabContainerCommon::selectPrevTab()
+{
+ BOOL tab_has_focus = FALSE;
+ if (mCurrentTabIdx >= 0 && mTabList[mCurrentTabIdx]->mButton->hasFocus())
+ {
+ tab_has_focus = TRUE;
+ }
+ S32 idx = mCurrentTabIdx-1;
+ if (idx < 0)
+ idx = mTabList.size()-1;
+ while (!selectTab(idx) && idx != mCurrentTabIdx)
+ {
+ idx = idx - 1;
+ if (idx < 0)
+ idx = mTabList.size()-1;
+ }
+ if (tab_has_focus)
+ {
+ mTabList[idx]->mButton->setFocus(TRUE);
+ }
+}
+
+
+void LLTabContainerCommon::reshape(S32 width, S32 height, BOOL called_from_parent)
+{
+ LLPanel::reshape( width, height, called_from_parent );
+ updateMaxScrollPos();
+}
+
+// static
+void LLTabContainerCommon::onTabBtn( void* userdata )
+{
+ LLTabTuple* tuple = (LLTabTuple*) userdata;
+ LLTabContainerCommon* self = tuple->mTabContainer;
+ self->selectTabPanel( tuple->mTabPanel );
+
+ if( tuple->mOnChangeCallback )
+ {
+ tuple->mOnChangeCallback( tuple->mUserData, true );
+ }
+
+ tuple->mTabPanel->setFocus(TRUE);
+}
+
+// static
+void LLTabContainerCommon::onCloseBtn( void* userdata )
+{
+ LLTabContainer* self = (LLTabContainer*) userdata;
+ if( self->mCloseCallback )
+ {
+ self->mCloseCallback( self->mCallbackUserdata );
+ }
+}
+
+// static
+void LLTabContainerCommon::onNextBtn( void* userdata )
+{
+ // Scroll tabs to the left
+ LLTabContainer* self = (LLTabContainer*) userdata;
+ if (!self->mScrolled)
+ {
+ self->scrollNext();
+ }
+ self->mScrolled = FALSE;
+}
+
+// static
+void LLTabContainerCommon::onNextBtnHeld( void* userdata )
+{
+ LLTabContainer* self = (LLTabContainer*) userdata;
+ if (self->mScrollTimer.getElapsedTimeF32() > SCROLL_STEP_TIME)
+ {
+ self->mScrollTimer.reset();
+ self->scrollNext();
+ self->mScrolled = TRUE;
+ }
+}
+
+// static
+void LLTabContainerCommon::onPrevBtn( void* userdata )
+{
+ LLTabContainer* self = (LLTabContainer*) userdata;
+ if (!self->mScrolled)
+ {
+ self->scrollPrev();
+ }
+ self->mScrolled = FALSE;
+}
+
+// static
+void LLTabContainerCommon::onPrevBtnHeld( void* userdata )
+{
+ LLTabContainer* self = (LLTabContainer*) userdata;
+ if (self->mScrollTimer.getElapsedTimeF32() > SCROLL_STEP_TIME)
+ {
+ self->mScrollTimer.reset();
+ self->scrollPrev();
+ self->mScrolled = TRUE;
+ }
+}
+
+BOOL LLTabContainerCommon::getTabPanelFlashing(LLPanel *child)
+{
+ LLTabTuple* tuple = getTabByPanel(child);
+ if( tuple )
+ {
+ return tuple->mButton->getFlashing();
+ }
+ return FALSE;
+}
+
+void LLTabContainerCommon::setTabPanelFlashing(LLPanel* child, BOOL state )
+{
+ LLTabTuple* tuple = getTabByPanel(child);
+ if( tuple )
+ {
+ tuple->mButton->setFlashing( state );
+ }
+}
+
+void LLTabContainerCommon::setTitle(const LLString& title)
+{
+ if (mTitleBox)
+ {
+ mTitleBox->setText( title );
+ }
+}
+
+const LLString LLTabContainerCommon::getPanelTitle(S32 index)
+{
+ if (index >= 0 && index < (S32)mTabList.size())
+ {
+ LLButton* tab_button = mTabList[index]->mButton;
+ return tab_button->getLabelSelected();
+ }
+ return LLString::null;
+}
+
+void LLTabContainerCommon::setTopBorderHeight(S32 height)
+{
+ mTopBorderHeight = height;
+}
+
+// Change the name of the button for the current tab.
+void LLTabContainerCommon::setCurrentTabName(const LLString& name)
+{
+ // Might not have a tab selected
+ if (mCurrentTabIdx < 0) return;
+
+ mTabList[mCurrentTabIdx]->mButton->setLabelSelected(name);
+ mTabList[mCurrentTabIdx]->mButton->setLabelUnselected(name);
+}
+
+LLView* LLTabContainerCommon::fromXML(LLXMLNodePtr node, LLView *parent, LLUICtrlFactory *factory)
+{
+ LLString name("tab_container");
+ node->getAttributeString("name", name);
+
+ // Figure out if we are creating a vertical or horizontal tab container.
+ bool is_vertical = false;
+ LLTabContainer::TabPosition tab_position = LLTabContainer::TOP;
+ if (node->hasAttribute("tab_position"))
+ {
+ LLString tab_position_string;
+ node->getAttributeString("tab_position", tab_position_string);
+ LLString::toLower(tab_position_string);
+
+ if ("top" == tab_position_string)
+ {
+ tab_position = LLTabContainer::TOP;
+ is_vertical = false;
+ }
+ else if ("bottom" == tab_position_string)
+ {
+ tab_position = LLTabContainer::BOTTOM;
+ is_vertical = false;
+ }
+ else if ("left" == tab_position_string)
+ {
+ is_vertical = true;
+ }
+ }
+ BOOL border = FALSE;
+ node->getAttributeBOOL("border", border);
+
+ // Create the correct container type.
+ LLTabContainerCommon* tab_container = NULL;
+
+ if (is_vertical)
+ {
+ // Vertical tabs can specify tab width
+ U32 tab_width = TABCNTRV_TAB_WIDTH;
+ if (node->hasAttribute("tab_width"))
+ {
+ node->getAttributeU32("tab_width", tab_width);
+ }
+
+ tab_container = new LLTabContainerVertical(name,
+ LLRect::null,
+ NULL,
+ NULL,
+ tab_width,
+ border);
+
+ }
+ else // horizontal tab container
+ {
+ // Horizontal tabs can have a title (?)
+ LLString title(LLString::null);
+ if (node->hasAttribute("title"))
+ {
+ node->getAttributeString("title", title);
+ }
+
+ tab_container = new LLTabContainer(name,
+ LLRect::null,
+ tab_position,
+ NULL,
+ NULL,
+ title,
+ border);
+
+ if(node->hasAttribute("tab_min_width"))
+ {
+ S32 minTabWidth=0;
+ node->getAttributeS32("tab_min_width",minTabWidth);
+ ((LLTabContainer*)tab_container)->setMinTabWidth(minTabWidth);
+ }
+ if(node->hasAttribute("tab_max_width"))
+ {
+ S32 maxTabWidth=0;
+ node->getAttributeS32("tab_max_width",maxTabWidth);
+ ((LLTabContainer*)tab_container)->setMaxTabWidth(maxTabWidth);
+ }
+ }
+
+ tab_container->setPanelParameters(node, parent);
+
+ if (LLFloater::getFloaterHost())
+ {
+ LLFloater::getFloaterHost()->setTabContainer(tab_container);
+ }
+
+ //parent->addChild(tab_container);
+
+ // Add all tab panels.
+ LLXMLNodePtr child;
+ for (child = node->getFirstChild(); child.notNull(); child = child->getNextSibling())
+ {
+ LLView *control = factory->createCtrlWidget(tab_container, child);
+ if (control && control->isPanel())
+ {
+ LLPanel* panelp = (LLPanel*)control;
+ LLString label;
+ child->getAttributeString("label", label);
+ if (label.empty())
+ {
+ label = panelp->getLabel();
+ }
+ BOOL placeholder = FALSE;
+ child->getAttributeBOOL("placeholder", placeholder);
+ tab_container->addTabPanel(panelp, label.c_str(), false,
+ NULL, NULL, 0, placeholder);
+ }
+ }
+
+ tab_container->selectFirstTab();
+
+ tab_container->postBuild();
+
+ tab_container->initButtons(); // now that we have the correct rect
+
+ return tab_container;
+}
+
+void LLTabContainerCommon::insertTuple(LLTabTuple * tuple, eInsertionPoint insertion_point)
+{
+ switch(insertion_point)
+ {
+ case START:
+ // insert the new tab in the front of the list
+ mTabList.insert(mTabList.begin(), tuple);
+ break;
+ case RIGHT_OF_CURRENT:
+ // insert the new tab after the current tab
+ {
+ tuple_list_t::iterator current_iter = mTabList.begin() + mCurrentTabIdx + 1;
+ mTabList.insert(current_iter, tuple);
+ }
+ break;
+ case END:
+ default:
+ mTabList.push_back( tuple );
+ }
+}
+
+
+LLTabContainer::LLTabContainer(
+ const LLString& name, const LLRect& rect, TabPosition pos,
+ void(*close_callback)(void*), void* callback_userdata,
+ const LLString& title, BOOL bordered )
+ :
+ LLTabContainerCommon(name, rect, pos, close_callback, callback_userdata, bordered),
+ mLeftArrowBtn(NULL),
+ mRightArrowBtn(NULL),
+ mRightTabBtnOffset(0),
+ mMinTabWidth(TABCNTR_TAB_MIN_WIDTH),
+ mMaxTabWidth(TABCNTR_TAB_MAX_WIDTH),
+ mTotalTabWidth(0)
+{
+ initButtons( );
+}
+
+LLTabContainer::LLTabContainer(
+ const LLString& name, const LLString& rect_control, TabPosition pos,
+ void(*close_callback)(void*), void* callback_userdata,
+ const LLString& title, BOOL bordered )
+ :
+ LLTabContainerCommon(name, rect_control, pos, close_callback, callback_userdata, bordered),
+ mLeftArrowBtn(NULL),
+ mRightArrowBtn(NULL),
+ mRightTabBtnOffset(0),
+ mMinTabWidth(TABCNTR_TAB_MIN_WIDTH),
+ mMaxTabWidth(TABCNTR_TAB_MAX_WIDTH),
+ mTotalTabWidth(0)
+{
+ initButtons( );
+}
+
+void LLTabContainer::initButtons()
+{
+ // Hack:
+ if (mRect.getHeight() == 0 || mLeftArrowBtn)
+ {
+ return; // Don't have a rect yet or already got called
+ }
+
+ LLString out_id;
+ LLString in_id;
+
+ S32 arrow_fudge = 1; // match new art better
+
+ // tabs on bottom reserve room for resize handle (just in case)
+ if (mTabPosition == BOTTOM)
+ {
+ mRightTabBtnOffset = RESIZE_HANDLE_WIDTH;
+ }
+
+ // Left and right scroll arrows (for when there are too many tabs to show all at once).
+ S32 btn_top = (mTabPosition == TOP ) ? mRect.getHeight() - mTopBorderHeight : TABCNTR_ARROW_BTN_SIZE + 1;
+
+ LLRect left_arrow_btn_rect;
+ left_arrow_btn_rect.setLeftTopAndSize( LLPANEL_BORDER_WIDTH+1, btn_top + arrow_fudge, TABCNTR_ARROW_BTN_SIZE, TABCNTR_ARROW_BTN_SIZE );
+
+ S32 right_pad = TABCNTR_ARROW_BTN_SIZE + LLPANEL_BORDER_WIDTH + 1;
+ LLRect right_arrow_btn_rect;
+ right_arrow_btn_rect.setLeftTopAndSize( mRect.getWidth() - mRightTabBtnOffset - right_pad,
+ btn_top + arrow_fudge,
+ TABCNTR_ARROW_BTN_SIZE, TABCNTR_ARROW_BTN_SIZE );
+
+ out_id = "UIImgBtnScrollLeftOutUUID";
+ in_id = "UIImgBtnScrollLeftInUUID";
+ mLeftArrowBtn = new LLButton(
+ "Left Arrow", left_arrow_btn_rect,
+ out_id, in_id, "",
+ &LLTabContainer::onPrevBtn, this, LLFontGL::sSansSerif );
+ mLeftArrowBtn->setHeldDownCallback(onPrevBtnHeld);
+ mLeftArrowBtn->setFollowsLeft();
+ mLeftArrowBtn->setSaveToXML(false);
+ mLeftArrowBtn->setTabStop(FALSE);
+ addChild(mLeftArrowBtn);
+
+ out_id = "UIImgBtnScrollRightOutUUID";
+ in_id = "UIImgBtnScrollRightInUUID";
+ mRightArrowBtn = new LLButton(
+ "Right Arrow", right_arrow_btn_rect,
+ out_id, in_id, "",
+ &LLTabContainer::onNextBtn, this,
+ LLFontGL::sSansSerif);
+ mRightArrowBtn->setFollowsRight();
+ mRightArrowBtn->setHeldDownCallback(onNextBtnHeld);
+ mRightArrowBtn->setSaveToXML(false);
+ mRightArrowBtn->setTabStop(FALSE);
+ addChild(mRightArrowBtn);
+
+ if( mTabPosition == TOP )
+ {
+ mRightArrowBtn->setFollowsTop();
+ mLeftArrowBtn->setFollowsTop();
+ }
+ else
+ {
+ mRightArrowBtn->setFollowsBottom();
+ mLeftArrowBtn->setFollowsBottom();
+ }
+
+ // set default tab group to be panel contents
+ mDefaultTabGroup = 1;
+}
+
+LLTabContainer::~LLTabContainer()
+{
+}
+
+void LLTabContainer::addTabPanel(LLPanel* child,
+ const LLString& label,
+ BOOL select,
+ void (*on_tab_clicked)(void*, bool),
+ void* userdata,
+ S32 indent,
+ BOOL placeholder,
+ eInsertionPoint insertion_point)
+{
+ if (child->getParent() == this)
+ {
+ // already a child of mine
+ return;
+ }
+ const LLFontGL* font = gResMgr->getRes( LLFONT_SANSSERIF_SMALL );
+
+ // Store the original label for possible xml export.
+ child->setLabel(label);
+ LLString trimmed_label = label;
+ LLString::trim(trimmed_label);
+
+ S32 button_width = llclamp(font->getWidth(trimmed_label) + TAB_PADDING, mMinTabWidth, mMaxTabWidth);
+
+ // Tab panel
+ S32 tab_panel_top;
+ S32 tab_panel_bottom;
+ if( LLTabContainer::TOP == mTabPosition )
+ {
+ tab_panel_top = mRect.getHeight() - mTopBorderHeight - (TABCNTR_TAB_HEIGHT - TABCNTR_BUTTON_PANEL_OVERLAP);
+ tab_panel_bottom = LLPANEL_BORDER_WIDTH;
+ }
+ else
+ {
+ tab_panel_top = mRect.getHeight() - mTopBorderHeight;
+ tab_panel_bottom = (TABCNTR_TAB_HEIGHT - TABCNTR_BUTTON_PANEL_OVERLAP); // Run to the edge, covering up the border
+ }
+
+ LLRect tab_panel_rect(
+ LLPANEL_BORDER_WIDTH,
+ tab_panel_top,
+ mRect.getWidth()-LLPANEL_BORDER_WIDTH,
+ tab_panel_bottom );
+
+ child->setFollowsAll();
+ child->translate( tab_panel_rect.mLeft - child->getRect().mLeft, tab_panel_rect.mBottom - child->getRect().mBottom);
+ child->reshape( tab_panel_rect.getWidth(), tab_panel_rect.getHeight(), TRUE );
+ child->setBackgroundVisible( FALSE ); // No need to overdraw
+ // add this child later
+
+ child->setVisible( FALSE ); // Will be made visible when selected
+
+ mTotalTabWidth += button_width;
+
+ // Tab button
+ LLRect btn_rect; // Note: btn_rect.mLeft is just a dummy. Will be updated in draw().
+ LLString tab_img;
+ LLString tab_selected_img;
+ S32 tab_fudge = 1; // To make new tab art look better, nudge buttons up 1 pel
+
+ if( LLTabContainer::TOP == mTabPosition )
+ {
+ btn_rect.setLeftTopAndSize( 0, mRect.getHeight() - mTopBorderHeight + tab_fudge, button_width, TABCNTR_TAB_HEIGHT );
+ tab_img = "UIImgBtnTabTopOutUUID";
+ tab_selected_img = "UIImgBtnTabTopInUUID";
+ }
+ else
+ {
+ btn_rect.setOriginAndSize( 0, 0 + tab_fudge, button_width, TABCNTR_TAB_HEIGHT );
+ tab_img = "UIImgBtnTabBottomOutUUID";
+ tab_selected_img = "UIImgBtnTabBottomInUUID";
+ }
+
+ if (placeholder)
+ {
+ //FIXME: wont work for horizontal tabs
+ btn_rect.translate(0, -LLBUTTON_V_PAD-2);
+ LLString box_label = trimmed_label;
+ LLTextBox* text = new LLTextBox(box_label, btn_rect, box_label, font);
+ addChild( text, 0 );
+
+ LLButton* btn = new LLButton("", LLRect(0,0,0,0));
+ LLTabTuple* tuple = new LLTabTuple( this, child, btn, on_tab_clicked, userdata, text );
+ addChild( btn, 0 );
+ addChild( child, 1 );
+ insertTuple(tuple, insertion_point);
+ }
+ else
+ {
+ LLString tooltip = trimmed_label;
+ tooltip += "\nCtrl-[ for previous tab";
+ tooltip += "\nCtrl-] for next tab";
+
+ LLButton* btn = new LLButton(
+ LLString(child->getName()) + " tab",
+ btn_rect,
+ tab_img, tab_selected_img, "",
+ &LLTabContainer::onTabBtn, NULL, // set userdata below
+ font,
+ trimmed_label, trimmed_label );
+ btn->setSaveToXML(false);
+ btn->setVisible( FALSE );
+ btn->setToolTip( tooltip );
+ btn->setScaleImage(TRUE);
+ btn->setFixedBorder(14, 14);
+
+ // Try to squeeze in a bit more text
+ btn->setLeftHPad( 4 );
+ btn->setRightHPad( 2 );
+ btn->setHAlign(LLFontGL::LEFT);
+ btn->setTabStop(FALSE);
+ if (indent)
+ {
+ btn->setLeftHPad(indent);
+ }
+
+ if( mTabPosition == TOP )
+ {
+ btn->setFollowsTop();
+ }
+ else
+ {
+ btn->setFollowsBottom();
+ }
+
+ LLTabTuple* tuple = new LLTabTuple( this, child, btn, on_tab_clicked, userdata );
+ btn->setCallbackUserData( tuple );
+ addChild( btn, 0 );
+ addChild( child, 1 );
+ insertTuple(tuple, insertion_point);
+ }
+
+ updateMaxScrollPos();
+
+ if( select )
+ {
+ selectLastTab();
+ }
+}
+
+void LLTabContainer::removeTabPanel(LLPanel* child)
+{
+ // Adjust the total tab width.
+ for(tuple_list_t::iterator iter = mTabList.begin(); iter != mTabList.end(); ++iter)
+ {
+ LLTabTuple* tuple = *iter;
+ if( tuple->mTabPanel == child )
+ {
+ mTotalTabWidth -= tuple->mButton->getRect().getWidth();
+ break;
+ }
+ }
+
+ LLTabContainerCommon::removeTabPanel(child);
+}
+
+void LLTabContainer::setPanelTitle(S32 index, const LLString& title)
+{
+ if (index >= 0 && index < (S32)mTabList.size())
+ {
+ LLButton* tab_button = mTabList[index]->mButton;
+ const LLFontGL* fontp = gResMgr->getRes( LLFONT_SANSSERIF_SMALL );
+ mTotalTabWidth -= tab_button->getRect().getWidth();
+ tab_button->reshape(llclamp(fontp->getWidth(title) + TAB_PADDING, mMinTabWidth, mMaxTabWidth), tab_button->getRect().getHeight());
+ mTotalTabWidth += tab_button->getRect().getWidth();
+ tab_button->setLabelSelected(title);
+ tab_button->setLabelUnselected(title);
+ }
+ updateMaxScrollPos();
+}
+
+
+void LLTabContainer::updateMaxScrollPos()
+{
+ S32 tab_space = 0;
+ S32 available_space = 0;
+ tab_space = mTotalTabWidth;
+ available_space = mRect.getWidth() - mRightTabBtnOffset - 2 * (LLPANEL_BORDER_WIDTH + TABCNTR_TAB_H_PAD);
+
+ if( tab_space > available_space )
+ {
+ S32 available_width_with_arrows = mRect.getWidth() - mRightTabBtnOffset - 2 * (LLPANEL_BORDER_WIDTH + TABCNTR_ARROW_BTN_SIZE + 1);
+ // subtract off reserved portion on left
+ available_width_with_arrows -= TABCNTR_TAB_PARTIAL_WIDTH;
+
+ S32 running_tab_width = 0;
+ mMaxScrollPos = mTabList.size();
+ for(tuple_list_t::reverse_iterator tab_it = mTabList.rbegin(); tab_it != mTabList.rend(); ++tab_it)
+ {
+ running_tab_width += (*tab_it)->mButton->getRect().getWidth();
+ if (running_tab_width > available_width_with_arrows)
+ {
+ break;
+ }
+ mMaxScrollPos--;
+ }
+ // in case last tab doesn't actually fit on screen, make it the last scrolling position
+ mMaxScrollPos = llmin(mMaxScrollPos, (S32)mTabList.size() - 1);
+ }
+ else
+ {
+ mMaxScrollPos = 0;
+ mScrollPos = 0;
+ }
+ if (mScrollPos > mMaxScrollPos)
+ {
+ mScrollPos = mMaxScrollPos;
+ }
+}
+
+void LLTabContainer::commitHoveredButton(S32 x, S32 y)
+{
+ if (gFocusMgr.getMouseCapture() == this)
+ {
+ for(tuple_list_t::iterator iter = mTabList.begin(); iter != mTabList.end(); ++iter)
+ {
+ LLTabTuple* tuple = *iter;
+ tuple->mButton->setVisible( TRUE );
+ S32 local_x = x - tuple->mButton->getRect().mLeft;
+ S32 local_y = y - tuple->mButton->getRect().mBottom;
+ if (tuple->mButton->pointInView(local_x, local_y) && tuple->mButton->getEnabled() && !tuple->mTabPanel->getVisible())
+ {
+ tuple->mButton->onCommit();
+ }
+ }
+ }
+}
+
+void LLTabContainer::setMinTabWidth(S32 width)
+{
+ mMinTabWidth = width;
+}
+
+void LLTabContainer::setMaxTabWidth(S32 width)
+{
+ mMaxTabWidth = width;
+}
+
+S32 LLTabContainer::getMinTabWidth() const
+{
+ return mMinTabWidth;
+}
+
+S32 LLTabContainer::getMaxTabWidth() const
+{
+ return mMaxTabWidth;
+}
+
+BOOL LLTabContainer::selectTab(S32 which)
+{
+ if (which >= (S32)mTabList.size()) return FALSE;
+ if (which < 0) return FALSE;
+
+ //if( gFocusMgr.childHasKeyboardFocus( this ) )
+ //{
+ // gFocusMgr.setKeyboardFocus( NULL, NULL );
+ //}
+
+ LLTabTuple* selected_tuple = mTabList[which];
+ if (!selected_tuple)
+ {
+ return FALSE;
+ }
+
+ if (mTabList[which]->mButton->getEnabled())
+ {
+ mCurrentTabIdx = which;
+
+ S32 i = 0;
+ for(tuple_list_t::iterator iter = mTabList.begin(); iter != mTabList.end(); ++iter)
+ {
+ LLTabTuple* tuple = *iter;
+ BOOL is_selected = ( tuple == selected_tuple );
+ tuple->mTabPanel->setVisible( is_selected );
+// tuple->mTabPanel->setFocus(is_selected); // not clear that we want to do this here.
+ tuple->mButton->setToggleState( is_selected );
+ // RN: this limits tab-stops to active button only, which would require arrow keys to switch tabs
+ tuple->mButton->setTabStop( is_selected && mTabList.size() > 1 );
+
+ if( is_selected && mMaxScrollPos > 0)
+ {
+ // Make sure selected tab is within scroll region
+ if( i < mScrollPos )
+ {
+ mScrollPos = i;
+ }
+ else
+ {
+ S32 available_width_with_arrows = mRect.getWidth() - mRightTabBtnOffset - 2 * (LLPANEL_BORDER_WIDTH + TABCNTR_ARROW_BTN_SIZE + 1);
+ S32 running_tab_width = tuple->mButton->getRect().getWidth();
+ S32 j = i - 1;
+ S32 min_scroll_pos = i;
+ if (running_tab_width < available_width_with_arrows)
+ {
+ while (j >= 0)
+ {
+ LLTabTuple* other_tuple = mTabList[j];
+ running_tab_width += other_tuple->mButton->getRect().getWidth();
+ if (running_tab_width > available_width_with_arrows)
+ {
+ break;
+ }
+ j--;
+ }
+ min_scroll_pos = j + 1;
+ }
+ mScrollPos = llclamp(mScrollPos, min_scroll_pos, i);
+ mScrollPos = llmin(mScrollPos, mMaxScrollPos);
+ }
+ }
+ i++;
+ }
+ if( selected_tuple->mOnChangeCallback )
+ {
+ selected_tuple->mOnChangeCallback( selected_tuple->mUserData, false );
+ }
+ return TRUE;
+ }
+ else
+ {
+ return FALSE;
+ }
+}
+
+void LLTabContainer::draw()
+{
+ S32 target_pixel_scroll = 0;
+ S32 cur_scroll_pos = mScrollPos;
+ if (cur_scroll_pos > 0)
+ {
+ S32 available_width_with_arrows = mRect.getWidth() - mRightTabBtnOffset - 2 * (LLPANEL_BORDER_WIDTH + TABCNTR_ARROW_BTN_SIZE + 1);
+ for(tuple_list_t::iterator iter = mTabList.begin(); iter != mTabList.end(); ++iter)
+ {
+ if (cur_scroll_pos == 0)
+ {
+ break;
+ }
+ target_pixel_scroll += (*iter)->mButton->getRect().getWidth();
+ cur_scroll_pos--;
+ }
+
+ // Show part of the tab to the left of what is fully visible
+ target_pixel_scroll -= TABCNTR_TAB_PARTIAL_WIDTH;
+ // clamp so that rightmost tab never leaves right side of screen
+ target_pixel_scroll = llmin(mTotalTabWidth - available_width_with_arrows, target_pixel_scroll);
+ }
+
+ mScrollPosPixels = (S32)lerp((F32)mScrollPosPixels, (F32)target_pixel_scroll, LLCriticalDamp::getInterpolant(0.08f));
+ if( getVisible() )
+ {
+ BOOL has_scroll_arrows = (mMaxScrollPos > 0) || (mScrollPosPixels > 0);
+ mLeftArrowBtn->setVisible( has_scroll_arrows );
+ mRightArrowBtn->setVisible( has_scroll_arrows );
+
+ // Set the leftmost position of the tab buttons.
+ S32 left = LLPANEL_BORDER_WIDTH + (has_scroll_arrows ? TABCNTR_ARROW_BTN_SIZE : TABCNTR_TAB_H_PAD);
+ left -= mScrollPosPixels;
+
+ // Hide all the buttons
+ for(tuple_list_t::iterator iter = mTabList.begin(); iter != mTabList.end(); ++iter)
+ {
+ LLTabTuple* tuple = *iter;
+ tuple->mButton->setVisible( FALSE );
+ }
+
+ LLPanel::draw();
+
+ // Show all the buttons
+ for(tuple_list_t::iterator iter = mTabList.begin(); iter != mTabList.end(); ++iter)
+ {
+ LLTabTuple* tuple = *iter;
+ tuple->mButton->setVisible( TRUE );
+ }
+
+ // Draw some of the buttons...
+
+ LLGLEnable scissor_test(has_scroll_arrows ? GL_SCISSOR_TEST : GL_FALSE);
+ if( has_scroll_arrows )
+ {
+ // ...but clip them.
+ S32 x1 = mLeftArrowBtn->getRect().mRight;
+ S32 y1 = 0;
+ S32 x2 = mRightArrowBtn->getRect().mLeft;
+ S32 y2 = 1;
+ if (mTabList.size() > 0)
+ {
+ y2 = mTabList[0]->mButton->getRect().mTop;
+ }
+ LLUI::setScissorRegionLocal(LLRect(x1, y2, x2, y1));
+ }
+
+ S32 max_scroll_visible = mTabList.size() - mMaxScrollPos + mScrollPos;
+ S32 idx = 0;
+ for(tuple_list_t::iterator iter = mTabList.begin(); iter != mTabList.end(); ++iter)
+ {
+ LLTabTuple* tuple = *iter;
+ tuple->mButton->translate( left - tuple->mButton->getRect().mLeft, 0 );
+ left += tuple->mButton->getRect().getWidth();
+
+ if( idx < mScrollPos )
+ {
+ if( tuple->mButton->getFlashing() )
+ {
+ mLeftArrowBtn->setFlashing( TRUE );
+ }
+ }
+ else
+ if( max_scroll_visible < idx )
+ {
+ if( tuple->mButton->getFlashing() )
+ {
+ mRightArrowBtn->setFlashing( TRUE );
+ }
+ }
+
+ LLUI::pushMatrix();
+ {
+ LLUI::translate((F32)tuple->mButton->getRect().mLeft, (F32)tuple->mButton->getRect().mBottom, 0.f);
+ tuple->mButton->draw();
+ }
+ LLUI::popMatrix();
+
+ idx++;
+ }
+
+ mLeftArrowBtn->setFlashing(FALSE);
+ mRightArrowBtn->setFlashing(FALSE);
+ }
+}
+
+
+void LLTabContainer::setRightTabBtnOffset(S32 offset)
+{
+ mRightArrowBtn->translate( -offset - mRightTabBtnOffset, 0 );
+ mRightTabBtnOffset = offset;
+ updateMaxScrollPos();
+}
+
+BOOL LLTabContainer::handleMouseDown( S32 x, S32 y, MASK mask )
+{
+ BOOL handled = FALSE;
+ BOOL has_scroll_arrows = (mMaxScrollPos > 0);
+
+ if (has_scroll_arrows)
+ {
+ if (mLeftArrowBtn->getRect().pointInRect(x, y))
+ {
+ S32 local_x = x - mLeftArrowBtn->getRect().mLeft;
+ S32 local_y = y - mLeftArrowBtn->getRect().mBottom;
+ handled = mLeftArrowBtn->handleMouseDown(local_x, local_y, mask);
+ }
+ else if (mRightArrowBtn->getRect().pointInRect(x, y))
+ {
+ S32 local_x = x - mRightArrowBtn->getRect().mLeft;
+ S32 local_y = y - mRightArrowBtn->getRect().mBottom;
+ handled = mRightArrowBtn->handleMouseDown(local_x, local_y, mask);
+ }
+ }
+ if (!handled)
+ {
+ handled = LLPanel::handleMouseDown( x, y, mask );
+ }
+
+ if (mTabList.size() > 0)
+ {
+ LLTabTuple* firsttuple = mTabList[0];
+ LLRect tab_rect(has_scroll_arrows ? mLeftArrowBtn->getRect().mRight : mLeftArrowBtn->getRect().mLeft,
+ firsttuple->mButton->getRect().mTop,
+ has_scroll_arrows ? mRightArrowBtn->getRect().mLeft : mRightArrowBtn->getRect().mRight,
+ firsttuple->mButton->getRect().mBottom );
+ if( tab_rect.pointInRect( x, y ) )
+ {
+ LLButton* tab_button = mTabList[getCurrentPanelIndex()]->mButton;
+ gFocusMgr.setMouseCapture(this, NULL);
+ gFocusMgr.setKeyboardFocus(tab_button, NULL);
+ }
+ }
+ return handled;
+}
+
+BOOL LLTabContainer::handleHover( S32 x, S32 y, MASK mask )
+{
+ BOOL handled = FALSE;
+ BOOL has_scroll_arrows = (mMaxScrollPos > 0);
+
+ if (has_scroll_arrows)
+ {
+ if (mLeftArrowBtn->getRect().pointInRect(x, y))
+ {
+ S32 local_x = x - mLeftArrowBtn->getRect().mLeft;
+ S32 local_y = y - mLeftArrowBtn->getRect().mBottom;
+ handled = mLeftArrowBtn->handleHover(local_x, local_y, mask);
+ }
+ else if (mRightArrowBtn->getRect().pointInRect(x, y))
+ {
+ S32 local_x = x - mRightArrowBtn->getRect().mLeft;
+ S32 local_y = y - mRightArrowBtn->getRect().mBottom;
+ handled = mRightArrowBtn->handleHover(local_x, local_y, mask);
+ }
+ }
+ if (!handled)
+ {
+ handled = LLPanel::handleHover(x, y, mask);
+ }
+
+ commitHoveredButton(x, y);
+ return handled;
+}
+
+BOOL LLTabContainer::handleMouseUp( S32 x, S32 y, MASK mask )
+{
+ BOOL handled = FALSE;
+ BOOL has_scroll_arrows = (mMaxScrollPos > 0);
+
+ if (has_scroll_arrows)
+ {
+ if (mLeftArrowBtn->getRect().pointInRect(x, y))
+ {
+ S32 local_x = x - mLeftArrowBtn->getRect().mLeft;
+ S32 local_y = y - mLeftArrowBtn->getRect().mBottom;
+ handled = mLeftArrowBtn->handleMouseUp(local_x, local_y, mask);
+ }
+ else if (mRightArrowBtn->getRect().pointInRect(x, y))
+ {
+ S32 local_x = x - mRightArrowBtn->getRect().mLeft;
+ S32 local_y = y - mRightArrowBtn->getRect().mBottom;
+ handled = mRightArrowBtn->handleMouseUp(local_x, local_y, mask);
+ }
+ }
+ if (!handled)
+ {
+ handled = LLPanel::handleMouseUp( x, y, mask );
+ }
+
+ commitHoveredButton(x, y);
+ LLPanel* cur_panel = getCurrentPanel();
+ if (gFocusMgr.getMouseCapture() == this)
+ {
+ if (cur_panel)
+ {
+ if (!cur_panel->focusFirstItem(FALSE))
+ {
+ // if nothing in the panel gets focus, make sure the new tab does
+ // otherwise the last tab might keep focus
+ mTabList[getCurrentPanelIndex()]->mButton->setFocus(TRUE);
+ }
+ }
+ gFocusMgr.setMouseCapture(NULL, NULL);
+ }
+ return handled;
+}
+
+BOOL LLTabContainer::handleToolTip( S32 x, S32 y, LLString& msg, LLRect* sticky_rect )
+{
+ BOOL handled = LLPanel::handleToolTip( x, y, msg, sticky_rect );
+ if (!handled && mTabList.size() > 0 && getVisible() && pointInView( x, y ) )
+ {
+ LLTabTuple* firsttuple = mTabList[0];
+
+ BOOL has_scroll_arrows = (mMaxScrollPos > 0);
+ LLRect clip(
+ has_scroll_arrows ? mLeftArrowBtn->getRect().mRight : mLeftArrowBtn->getRect().mLeft,
+ firsttuple->mButton->getRect().mTop,
+ has_scroll_arrows ? mRightArrowBtn->getRect().mLeft : mRightArrowBtn->getRect().mRight,
+ 0 );
+ if( clip.pointInRect( x, y ) )
+ {
+ for(tuple_list_t::iterator iter = mTabList.begin(); iter != mTabList.end(); ++iter)
+ {
+ LLTabTuple* tuple = *iter;
+ tuple->mButton->setVisible( TRUE );
+ S32 local_x = x - tuple->mButton->getRect().mLeft;
+ S32 local_y = y - tuple->mButton->getRect().mBottom;
+ handled = tuple->mButton->handleToolTip( local_x, local_y, msg, sticky_rect );
+ if( handled )
+ {
+ break;
+ }
+ }
+ }
+
+ for(tuple_list_t::iterator iter = mTabList.begin(); iter != mTabList.end(); ++iter)
+ {
+ LLTabTuple* tuple = *iter;
+ tuple->mButton->setVisible( FALSE );
+ }
+ }
+ return handled;
+}
+
+BOOL LLTabContainer::handleKeyHere(KEY key, MASK mask, BOOL called_from_parent)
+{
+ if (!getEnabled()) return FALSE;
+
+ if (!gFocusMgr.childHasKeyboardFocus(this)) return FALSE;
+
+ BOOL handled = FALSE;
+ if (key == '[' && mask == MASK_CONTROL)
+ {
+ selectPrevTab();
+ handled = TRUE;
+ }
+ else if (key == ']' && mask == MASK_CONTROL)
+ {
+ selectNextTab();
+ handled = TRUE;
+ }
+
+ if (handled)
+ {
+ if (getCurrentPanel())
+ {
+ getCurrentPanel()->setFocus(TRUE);
+ }
+ }
+
+ if (!gFocusMgr.childHasKeyboardFocus(getCurrentPanel()))
+ {
+ // if child has focus, but not the current panel, focus
+ // is on a button
+ switch(key)
+ {
+ case KEY_UP:
+ if (getTabPosition() == BOTTOM && getCurrentPanel())
+ {
+ getCurrentPanel()->setFocus(TRUE);
+ }
+ handled = TRUE;
+ break;
+ case KEY_DOWN:
+ if (getTabPosition() == TOP && getCurrentPanel())
+ {
+ getCurrentPanel()->setFocus(TRUE);
+ }
+ handled = TRUE;
+ break;
+ case KEY_LEFT:
+ selectPrevTab();
+ handled = TRUE;
+ break;
+ case KEY_RIGHT:
+ selectNextTab();
+ handled = TRUE;
+ break;
+ default:
+ break;
+ }
+ }
+ return handled;
+}
+
+// virtual
+LLXMLNodePtr LLTabContainer::getXML(bool save_children) const
+{
+ LLXMLNodePtr node = LLTabContainerCommon::getXML();
+
+ node->createChild("tab_position", TRUE)->setStringValue((mTabPosition == TOP ? "top" : "bottom"));
+
+ return node;
+}
+
+BOOL LLTabContainer::handleDragAndDrop(S32 x, S32 y, MASK mask, BOOL drop, EDragAndDropType type, void* cargo_data, EAcceptance *accept, LLString &tooltip)
+{
+ BOOL has_scroll_arrows = (mMaxScrollPos > 0);
+
+ if (has_scroll_arrows)
+ {
+ if (mLeftArrowBtn->getRect().pointInRect(x, y))
+ {
+ S32 local_x = x - mLeftArrowBtn->getRect().mLeft;
+ S32 local_y = y - mLeftArrowBtn->getRect().mBottom;
+ mLeftArrowBtn->handleHover(local_x, local_y, mask);
+ }
+ else if (mRightArrowBtn->getRect().pointInRect(x, y))
+ {
+ S32 local_x = x - mRightArrowBtn->getRect().mLeft;
+ S32 local_y = y - mRightArrowBtn->getRect().mBottom;
+ mRightArrowBtn->handleHover(local_x, local_y, mask);
+ }
+ }
+
+ for(tuple_list_t::iterator iter = mTabList.begin(); iter != mTabList.end(); ++iter)
+ {
+ LLTabTuple* tuple = *iter;
+ tuple->mButton->setVisible( TRUE );
+ S32 local_x = x - tuple->mButton->getRect().mLeft;
+ S32 local_y = y - tuple->mButton->getRect().mBottom;
+ if (tuple->mButton->pointInView(local_x, local_y) && tuple->mButton->getEnabled() && !tuple->mTabPanel->getVisible())
+ {
+ tuple->mButton->onCommit();
+ }
+ }
+
+ return LLView::handleDragAndDrop(x, y, mask, drop, type, cargo_data, accept, tooltip);
+}
+