From 2e32d44e7165775936beae5d9ef636ff9d3f2bd2 Mon Sep 17 00:00:00 2001 From: Steven Bennetts Date: Tue, 19 Feb 2008 21:42:32 +0000 Subject: merge svn+ssh://svn.lindenlab.com/svn/linden/qa/combo-merge-ui-2008-02-13 -r 79986 : 80178 -> release. QAR-290 = QAR-271 + QAR-191 --- indra/llui/lltabcontainer.cpp | 2515 ++++++++++++++++++++++------------------- 1 file changed, 1325 insertions(+), 1190 deletions(-) (limited to 'indra/llui/lltabcontainer.cpp') diff --git a/indra/llui/lltabcontainer.cpp b/indra/llui/lltabcontainer.cpp index 148060359f..4568ebac29 100644 --- a/indra/llui/lltabcontainer.cpp +++ b/indra/llui/lltabcontainer.cpp @@ -1,6 +1,6 @@ /** * @file lltabcontainer.cpp - * @brief LLTabContainerCommon base class + * @brief LLTabContainer class * * $LicenseInfo:firstyear=2001&license=viewergpl$ * @@ -30,33 +30,23 @@ */ #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 F32 SCROLL_DELAY_TIME = 0.5f; const S32 TAB_PADDING = 15; const S32 TABCNTR_TAB_MIN_WIDTH = 60; +const S32 TABCNTR_VERT_TAB_MIN_WIDTH = 100; 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; @@ -64,12 +54,19 @@ 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; +const S32 TABCNTR_CLOSE_BTN_SIZE = 16; +const S32 TABCNTR_HEADER_HEIGHT = LLPANEL_BORDER_WIDTH + TABCNTR_CLOSE_BTN_SIZE; -LLTabContainerCommon::LLTabContainerCommon( - const LLString& name, const LLRect& rect, - TabPosition pos, - void(*close_callback)(void*), void* callback_userdata, - BOOL bordered ) +const S32 TABCNTRV_CLOSE_BTN_SIZE = 16; +const S32 TABCNTRV_HEADER_HEIGHT = LLPANEL_BORDER_WIDTH + TABCNTRV_CLOSE_BTN_SIZE; +//const S32 TABCNTRV_TAB_WIDTH = 100; +const S32 TABCNTRV_ARROW_BTN_SIZE = 16; +const S32 TABCNTRV_PAD = 0; + + + +LLTabContainer::LLTabContainer(const LLString& name, const LLRect& rect, TabPosition pos, + BOOL bordered, BOOL is_vertical ) : LLPanel(name, rect, bordered), mCurrentTabIdx(-1), @@ -78,50 +75,65 @@ LLTabContainerCommon::LLTabContainerCommon( mScrollPos(0), mScrollPosPixels(0), mMaxScrollPos(0), - mCloseCallback( close_callback ), - mCallbackUserdata( callback_userdata ), + mCloseCallback( NULL ), + mCallbackUserdata( NULL ), mTitleBox(NULL), mTopBorderHeight(LLPANEL_BORDER_WIDTH), mTabPosition(pos), - mLockedTabCount(0) + mLockedTabCount(0), + mMinTabWidth(TABCNTR_TAB_MIN_WIDTH), + mMaxTabWidth(TABCNTR_TAB_MAX_WIDTH), + mPrevArrowBtn(NULL), + mNextArrowBtn(NULL), + mIsVertical(is_vertical), + // Horizontal Specific + mJumpPrevArrowBtn(NULL), + mJumpNextArrowBtn(NULL), + mRightTabBtnOffset(0), + mTotalTabWidth(0) { + //RN: HACK to support default min width for legacy vertical tab containers + if (mIsVertical) + { + mMinTabWidth = TABCNTR_VERT_TAB_MIN_WIDTH; + } setMouseOpaque(FALSE); + initButtons( ); mDragAndDropDelayTimer.stop(); } +LLTabContainer::~LLTabContainer() +{ + std::for_each(mTabList.begin(), mTabList.end(), DeletePointer()); +} -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), - mTabsHidden(FALSE), - mScrolled(FALSE), - mScrollPos(0), - mScrollPosPixels(0), - mMaxScrollPos(0), - mCloseCallback( close_callback ), - mCallbackUserdata( callback_userdata ), - mTitleBox(NULL), - mTopBorderHeight(LLPANEL_BORDER_WIDTH), - mTabPosition(pos), - mLockedTabCount(0) +//virtual +void LLTabContainer::setValue(const LLSD& value) { - setMouseOpaque(FALSE); - mDragAndDropDelayTimer.stop(); + selectTab((S32) value.asInteger()); +} + +//virtual +EWidgetType LLTabContainer::getWidgetType() const +{ + return WIDGET_TYPE_TAB_CONTAINER; } +//virtual +LLString LLTabContainer::getWidgetTag() const +{ + return LL_TAB_CONTAINER_COMMON_TAG; +} -LLTabContainerCommon::~LLTabContainerCommon() +//virtual +void LLTabContainer::reshape(S32 width, S32 height, BOOL called_from_parent) { - std::for_each(mTabList.begin(), mTabList.end(), DeletePointer()); + LLPanel::reshape( width, height, called_from_parent ); + updateMaxScrollPos(); } -LLView* LLTabContainerCommon::getChildByName(const LLString& name, BOOL recurse) const +//virtual +LLView* LLTabContainer::getChildByName(const LLString& name, BOOL recurse) const { tuple_list_t::const_iterator itor; for (itor = mTabList.begin(); itor != mTabList.end(); ++itor) @@ -137,7 +149,7 @@ LLView* LLTabContainerCommon::getChildByName(const LLString& name, BOOL recurse) for (itor = mTabList.begin(); itor != mTabList.end(); ++itor) { LLPanel *panel = (*itor)->mTabPanel; - LLView *child = panel->getChildByName(name, recurse); + LLView *child = panel->getChild(name, recurse); if (child) { return child; @@ -147,754 +159,514 @@ LLView* LLTabContainerCommon::getChildByName(const LLString& name, BOOL recurse) return LLView::getChildByName(name, recurse); } -void LLTabContainerCommon::addPlaceholder(LLPanel* child, const LLString& label) -{ - addTabPanel(child, label, FALSE, NULL, NULL, 0, TRUE); -} - -void LLTabContainerCommon::lockTabs(S32 num_tabs) -{ - // count current tabs or use supplied value and ensure no new tabs get - // inserted between them - mLockedTabCount = num_tabs > 0 ? llmin(getTabCount(), num_tabs) : getTabCount(); -} - -void LLTabContainerCommon::unlockTabs() -{ - mLockedTabCount = 0; -} - -void LLTabContainerCommon::removeTabPanel(LLPanel* child) +// virtual +void LLTabContainer::draw() { - BOOL has_focus = gFocusMgr.childHasKeyboardFocus(this); - - // If the tab being deleted is the selected one, select a different tab. - for(std::vector::iterator iter = mTabList.begin(); iter != mTabList.end(); ++iter) + S32 target_pixel_scroll = 0; + S32 cur_scroll_pos = mIsVertical ? 0 : getScrollPos(); + if (cur_scroll_pos > 0) { - LLTabTuple* tuple = *iter; - if( tuple->mTabPanel == child ) + S32 available_width_with_arrows = getRect().getWidth() - mRightTabBtnOffset - 2 * (LLPANEL_BORDER_WIDTH + TABCNTR_ARROW_BTN_SIZE + TABCNTR_ARROW_BTN_SIZE + 1); + for(tuple_list_t::iterator iter = mTabList.begin(); iter != mTabList.end(); ++iter) { - removeChild( tuple->mButton ); - delete tuple->mButton; - - removeChild( tuple->mTabPanel ); -// delete tuple->mTabPanel; - - mTabList.erase( iter ); - delete tuple; - - break; + if (cur_scroll_pos == 0) + { + break; + } + target_pixel_scroll += (*iter)->mButton->getRect().getWidth(); + cur_scroll_pos--; } - } - // make sure we don't have more locked tabs than we have tabs - mLockedTabCount = llmin(getTabCount(), mLockedTabCount); - - if (mCurrentTabIdx >= (S32)mTabList.size()) - { - mCurrentTabIdx = mTabList.size()-1; + // 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); } - selectTab(mCurrentTabIdx); - if (has_focus) + + setScrollPosPixels((S32)lerp((F32)getScrollPosPixels(), (F32)target_pixel_scroll, LLCriticalDamp::getInterpolant(0.08f))); + if( getVisible() ) { - LLPanel* panelp = getPanelByIndex(mCurrentTabIdx); - if (panelp) + BOOL has_scroll_arrows = (mMaxScrollPos > 0) || (mScrollPosPixels > 0); + if (!mIsVertical) { - panelp->setFocus(TRUE); + mJumpPrevArrowBtn->setVisible( has_scroll_arrows ); + mJumpNextArrowBtn->setVisible( has_scroll_arrows ); } - } + mPrevArrowBtn->setVisible( has_scroll_arrows ); + mNextArrowBtn->setVisible( has_scroll_arrows ); - updateMaxScrollPos(); -} + S32 left = 0, top = 0; + if (mIsVertical) + { + top = getRect().getHeight() - getTopBorderHeight() - LLPANEL_BORDER_WIDTH - 1 - (has_scroll_arrows ? TABCNTRV_ARROW_BTN_SIZE : 0); + top += getScrollPosPixels(); + } + else + { + // Set the leftmost position of the tab buttons. + left = LLPANEL_BORDER_WIDTH + (has_scroll_arrows ? (TABCNTR_ARROW_BTN_SIZE * 2) : TABCNTR_TAB_H_PAD); + left -= getScrollPosPixels(); + } + + // Hide all the buttons + for(tuple_list_t::iterator iter = mTabList.begin(); iter != mTabList.end(); ++iter) + { + LLTabTuple* tuple = *iter; + tuple->mButton->setVisible( FALSE ); + } -void LLTabContainerCommon::deleteAllTabs() -{ - // Remove all the tab buttons and delete them. Also, unlink all the child panels. - for(std::vector::iterator iter = mTabList.begin(); iter != mTabList.end(); ++iter) - { - LLTabTuple* tuple = *iter; + LLPanel::draw(); - removeChild( tuple->mButton ); - delete tuple->mButton; + // if tabs are hidden, don't draw them and leave them in the invisible state + if (!getTabsHidden()) + { + // Show all the buttons + for(tuple_list_t::iterator iter = mTabList.begin(); iter != mTabList.end(); ++iter) + { + LLTabTuple* tuple = *iter; + tuple->mButton->setVisible( TRUE ); + } - removeChild( tuple->mTabPanel ); -// delete tuple->mTabPanel; - } + // Draw some of the buttons... + LLRect clip_rect = getLocalRect(); + if (has_scroll_arrows) + { + // ...but clip them. + if (mIsVertical) + { + clip_rect.mBottom = mNextArrowBtn->getRect().mTop + 3*TABCNTRV_PAD; + clip_rect.mTop = mPrevArrowBtn->getRect().mBottom - 3*TABCNTRV_PAD; + } + else + { + clip_rect.mLeft = mPrevArrowBtn->getRect().mRight; + clip_rect.mRight = mNextArrowBtn->getRect().mLeft; + } + } + LLLocalClipRect clip(clip_rect); - // 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; -} + S32 max_scroll_visible = getTabCount() - getMaxScrollPos() + getScrollPos(); + S32 idx = 0; + for(tuple_list_t::iterator iter = mTabList.begin(); iter != mTabList.end(); ++iter) + { + LLTabTuple* tuple = *iter; + tuple->mButton->translate( left ? left - tuple->mButton->getRect().mLeft : 0, + top ? top - tuple->mButton->getRect().mTop : 0 ); + if (top) top -= BTN_HEIGHT + TABCNTRV_PAD; + if (left) left += tuple->mButton->getRect().getWidth(); -LLPanel* LLTabContainerCommon::getCurrentPanel() -{ - if (mCurrentTabIdx < 0 || mCurrentTabIdx >= (S32) mTabList.size()) return NULL; - - return mTabList[mCurrentTabIdx]->mTabPanel; -} + if (!mIsVertical) + { + if( idx < getScrollPos() ) + { + if( tuple->mButton->getFlashing() ) + { + mPrevArrowBtn->setFlashing( TRUE ); + } + } + else if( max_scroll_visible < idx ) + { + if( tuple->mButton->getFlashing() ) + { + mNextArrowBtn->setFlashing( TRUE ); + } + } + } + LLUI::pushMatrix(); + { + LLUI::translate((F32)tuple->mButton->getRect().mLeft, (F32)tuple->mButton->getRect().mBottom, 0.f); + tuple->mButton->draw(); + } + LLUI::popMatrix(); -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; -} + idx++; + } -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; - } -} + if( mIsVertical && has_scroll_arrows ) + { + // Redraw the arrows so that they appears on top. + glPushMatrix(); + glTranslatef((F32)mPrevArrowBtn->getRect().mLeft, (F32)mPrevArrowBtn->getRect().mBottom, 0.f); + mPrevArrowBtn->draw(); + glPopMatrix(); + + glPushMatrix(); + glTranslatef((F32)mNextArrowBtn->getRect().mLeft, (F32)mNextArrowBtn->getRect().mBottom, 0.f); + mNextArrowBtn->draw(); + glPopMatrix(); + } + } -S32 LLTabContainerCommon::getCurrentPanelIndex() -{ - return mCurrentTabIdx; + mPrevArrowBtn->setFlashing(FALSE); + mNextArrowBtn->setFlashing(FALSE); + } } -S32 LLTabContainerCommon::getTabCount() -{ - return mTabList.size(); -} -LLPanel* LLTabContainerCommon::getPanelByIndex(S32 index) +// virtual +BOOL LLTabContainer::handleMouseDown( S32 x, S32 y, MASK mask ) { - if (index >= 0 && index < (S32)mTabList.size()) - { - return mTabList[index]->mTabPanel; - } - return NULL; -} + BOOL handled = FALSE; + BOOL has_scroll_arrows = (getMaxScrollPos() > 0); -S32 LLTabContainerCommon::getIndexForPanel(LLPanel* panel) -{ - for (S32 index = 0; index < (S32)mTabList.size(); index++) + if (has_scroll_arrows) { - if (mTabList[index]->mTabPanel == panel) + if (mJumpPrevArrowBtn&& mJumpPrevArrowBtn->getRect().pointInRect(x, y)) { - return index; + S32 local_x = x - mJumpPrevArrowBtn->getRect().mLeft; + S32 local_y = y - mJumpPrevArrowBtn->getRect().mBottom; + handled = mJumpPrevArrowBtn->handleMouseDown(local_x, local_y, mask); + } + else if (mJumpNextArrowBtn && mJumpNextArrowBtn->getRect().pointInRect(x, y)) + { + S32 local_x = x - mJumpNextArrowBtn->getRect().mLeft; + S32 local_y = y - mJumpNextArrowBtn->getRect().mBottom; + handled = mJumpNextArrowBtn->handleMouseDown(local_x, local_y, mask); + } + else if (mPrevArrowBtn && mPrevArrowBtn->getRect().pointInRect(x, y)) + { + S32 local_x = x - mPrevArrowBtn->getRect().mLeft; + S32 local_y = y - mPrevArrowBtn->getRect().mBottom; + handled = mPrevArrowBtn->handleMouseDown(local_x, local_y, mask); + } + else if (mNextArrowBtn && mNextArrowBtn->getRect().pointInRect(x, y)) + { + S32 local_x = x - mNextArrowBtn->getRect().mLeft; + S32 local_y = y - mNextArrowBtn->getRect().mBottom; + handled = mNextArrowBtn->handleMouseDown(local_x, local_y, mask); } } - return -1; -} + if (!handled) + { + handled = LLPanel::handleMouseDown( x, y, mask ); + } -S32 LLTabContainerCommon::getPanelIndexByTitle(const LLString& title) -{ - for (S32 index = 0 ; index < (S32)mTabList.size(); index++) + if (getTabCount() > 0) { - if (title == mTabList[index]->mButton->getLabelSelected()) + LLTabTuple* firsttuple = getTab(0); + LLRect tab_rect; + if (mIsVertical) { - return index; + tab_rect = LLRect(firsttuple->mButton->getRect().mLeft, + has_scroll_arrows ? mPrevArrowBtn->getRect().mBottom - TABCNTRV_PAD : mPrevArrowBtn->getRect().mTop, + firsttuple->mButton->getRect().mRight, + has_scroll_arrows ? mNextArrowBtn->getRect().mTop + TABCNTRV_PAD : mNextArrowBtn->getRect().mBottom ); + } + else + { + tab_rect = LLRect(has_scroll_arrows ? mPrevArrowBtn->getRect().mRight : mJumpPrevArrowBtn->getRect().mLeft, + firsttuple->mButton->getRect().mTop, + has_scroll_arrows ? mNextArrowBtn->getRect().mLeft : mJumpNextArrowBtn->getRect().mRight, + firsttuple->mButton->getRect().mBottom ); + } + if( tab_rect.pointInRect( x, y ) ) + { + LLButton* tab_button = getTab(getCurrentPanelIndex())->mButton; + gFocusMgr.setMouseCapture(this); + gFocusMgr.setKeyboardFocus(tab_button); } } - return -1; + return handled; } -LLPanel *LLTabContainerCommon::getPanelByName(const LLString& name) +// virtual +BOOL LLTabContainer::handleHover( S32 x, S32 y, MASK mask ) { - for (S32 index = 0 ; index < (S32)mTabList.size(); index++) + BOOL handled = FALSE; + BOOL has_scroll_arrows = (getMaxScrollPos() > 0); + + if (has_scroll_arrows) { - LLPanel *panel = mTabList[index]->mTabPanel; - if (name == panel->getName()) + if (mJumpPrevArrowBtn && mJumpPrevArrowBtn->getRect().pointInRect(x, y)) { - return panel; + S32 local_x = x - mJumpPrevArrowBtn->getRect().mLeft; + S32 local_y = y - mJumpPrevArrowBtn->getRect().mBottom; + handled = mJumpPrevArrowBtn->handleHover(local_x, local_y, mask); } - } - 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 ) + else if (mJumpNextArrowBtn && mJumpNextArrowBtn->getRect().pointInRect(x, y)) { - return selectTab( idx ); + S32 local_x = x - mJumpNextArrowBtn->getRect().mLeft; + S32 local_y = y - mJumpNextArrowBtn->getRect().mBottom; + handled = mJumpNextArrowBtn->handleHover(local_x, local_y, mask); + } + else if (mPrevArrowBtn && mPrevArrowBtn->getRect().pointInRect(x, y)) + { + S32 local_x = x - mPrevArrowBtn->getRect().mLeft; + S32 local_y = y - mPrevArrowBtn->getRect().mBottom; + handled = mPrevArrowBtn->handleHover(local_x, local_y, mask); + } + else if (mNextArrowBtn && mNextArrowBtn->getRect().pointInRect(x, y)) + { + S32 local_x = x - mNextArrowBtn->getRect().mLeft; + S32 local_y = y - mNextArrowBtn->getRect().mBottom; + handled = mNextArrowBtn->handleHover(local_x, local_y, mask); } - 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; -} - - -void LLTabContainerCommon::onJumpFirstBtn( void* userdata ) -{ - LLTabContainer* self = (LLTabContainer*) userdata; - - self->mScrollPos = 0; - -} - - -void LLTabContainerCommon::onJumpLastBtn( void* userdata ) -{ - LLTabContainer* self = (LLTabContainer*) userdata; - - self->mScrollPos = self->mMaxScrollPos; -} - - -// static -void LLTabContainerCommon::onPrevBtnHeld( void* userdata ) -{ - LLTabContainer* self = (LLTabContainer*) userdata; - if (self->mScrollTimer.getElapsedTimeF32() > SCROLL_STEP_TIME) + if (!handled) { - self->mScrollTimer.reset(); - self->scrollPrev(); - self->mScrolled = TRUE; + handled = LLPanel::handleHover(x, y, mask); } -} -BOOL LLTabContainerCommon::getTabPanelFlashing(LLPanel *child) -{ - LLTabTuple* tuple = getTabByPanel(child); - if( tuple ) - { - return tuple->mButton->getFlashing(); - } - return FALSE; + commitHoveredButton(x, y); + return handled; } -void LLTabContainerCommon::setTabPanelFlashing(LLPanel* child, BOOL state ) +// virtual +BOOL LLTabContainer::handleMouseUp( S32 x, S32 y, MASK mask ) { - LLTabTuple* tuple = getTabByPanel(child); - if( tuple ) - { - tuple->mButton->setFlashing( state ); - } -} + BOOL handled = FALSE; + BOOL has_scroll_arrows = (getMaxScrollPos() > 0); -void LLTabContainerCommon::setTabImage(LLPanel* child, std::string img_name, const LLColor4& color) -{ - LLTabTuple* tuple = getTabByPanel(child); - if( tuple ) + if (has_scroll_arrows) { - tuple->mButton->setImageOverlay(img_name, LLFontGL::RIGHT, color); + if (mJumpPrevArrowBtn && mJumpPrevArrowBtn->getRect().pointInRect(x, y)) + { + S32 local_x = x - mJumpPrevArrowBtn->getRect().mLeft; + S32 local_y = y - mJumpPrevArrowBtn->getRect().mBottom; + handled = mJumpPrevArrowBtn->handleMouseUp(local_x, local_y, mask); + } + else if (mJumpNextArrowBtn && mJumpNextArrowBtn->getRect().pointInRect(x, y)) + { + S32 local_x = x - mJumpNextArrowBtn->getRect().mLeft; + S32 local_y = y - mJumpNextArrowBtn->getRect().mBottom; + handled = mJumpNextArrowBtn->handleMouseUp(local_x, local_y, mask); + } + else if (mPrevArrowBtn && mPrevArrowBtn->getRect().pointInRect(x, y)) + { + S32 local_x = x - mPrevArrowBtn->getRect().mLeft; + S32 local_y = y - mPrevArrowBtn->getRect().mBottom; + handled = mPrevArrowBtn->handleMouseUp(local_x, local_y, mask); + } + else if (mNextArrowBtn && mNextArrowBtn->getRect().pointInRect(x, y)) + { + S32 local_x = x - mNextArrowBtn->getRect().mLeft; + S32 local_y = y - mNextArrowBtn->getRect().mBottom; + handled = mNextArrowBtn->handleMouseUp(local_x, local_y, mask); + } } -} - -void LLTabContainerCommon::setTitle(const LLString& title) -{ - if (mTitleBox) + if (!handled) { - mTitleBox->setText( title ); + handled = LLPanel::handleMouseUp( x, y, mask ); } -} -const LLString LLTabContainerCommon::getPanelTitle(S32 index) -{ - if (index >= 0 && index < (S32)mTabList.size()) + commitHoveredButton(x, y); + LLPanel* cur_panel = getCurrentPanel(); + if (hasMouseCapture()) { - LLButton* tab_button = mTabList[index]->mButton; - return tab_button->getLabelSelected(); + 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 + getTab(getCurrentPanelIndex())->mButton->setFocus(TRUE); + } + } + gFocusMgr.setMouseCapture(NULL); } - 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); + return handled; } -LLView* LLTabContainerCommon::fromXML(LLXMLNodePtr node, LLView *parent, LLUICtrlFactory *factory) +// virtual +BOOL LLTabContainer::handleToolTip( S32 x, S32 y, LLString& msg, LLRect* sticky_rect ) { - 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")) + BOOL handled = LLPanel::handleToolTip( x, y, msg, sticky_rect ); + if (!handled && getTabCount() > 0) { - LLString tab_position_string; - node->getAttributeString("tab_position", tab_position_string); - LLString::toLower(tab_position_string); + LLTabTuple* firsttuple = getTab(0); - if ("top" == tab_position_string) - { - tab_position = LLTabContainer::TOP; - is_vertical = false; - } - else if ("bottom" == tab_position_string) + BOOL has_scroll_arrows = (getMaxScrollPos() > 0); + LLRect clip; + if (mIsVertical) { - tab_position = LLTabContainer::BOTTOM; - is_vertical = false; + clip = LLRect(firsttuple->mButton->getRect().mLeft, + has_scroll_arrows ? mPrevArrowBtn->getRect().mBottom - TABCNTRV_PAD : mPrevArrowBtn->getRect().mTop, + firsttuple->mButton->getRect().mRight, + has_scroll_arrows ? mNextArrowBtn->getRect().mTop + TABCNTRV_PAD : mNextArrowBtn->getRect().mBottom ); } - 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")) + else { - node->getAttributeU32("tab_width", tab_width); + clip = LLRect(has_scroll_arrows ? mPrevArrowBtn->getRect().mRight : mJumpPrevArrowBtn->getRect().mLeft, + firsttuple->mButton->getRect().mTop, + has_scroll_arrows ? mNextArrowBtn->getRect().mLeft : mJumpNextArrowBtn->getRect().mRight, + firsttuple->mButton->getRect().mBottom ); } - 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")) + if( clip.pointInRect( x, y ) ) { - node->getAttributeString("title", title); + 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; + } + } } - 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")) + for(tuple_list_t::iterator iter = mTabList.begin(); iter != mTabList.end(); ++iter) { - S32 maxTabWidth=0; - node->getAttributeS32("tab_max_width",maxTabWidth); - ((LLTabContainer*)tab_container)->setMaxTabWidth(maxTabWidth); + LLTabTuple* tuple = *iter; + tuple->mButton->setVisible( FALSE ); } } - - node->getAttributeBOOL("hide_tabs", tab_container->mTabsHidden); + return handled; +} - tab_container->setPanelParameters(node, parent); +// virtual +BOOL LLTabContainer::handleKeyHere(KEY key, MASK mask, BOOL called_from_parent) +{ + if (!getEnabled()) return FALSE; - if (LLFloater::getFloaterHost()) + if (!gFocusMgr.childHasKeyboardFocus(this)) return FALSE; + + BOOL handled = FALSE; + if (key == KEY_LEFT && mask == MASK_ALT) { - LLFloater::getFloaterHost()->setTabContainer(tab_container); + selectPrevTab(); + handled = TRUE; + } + else if (key == KEY_RIGHT && mask == MASK_ALT) + { + selectNextTab(); + handled = TRUE; } - //parent->addChild(tab_container); - - // Add all tab panels. - LLXMLNodePtr child; - for (child = node->getFirstChild(); child.notNull(); child = child->getNextSibling()) + if (handled) { - LLView *control = factory->createCtrlWidget(tab_container, child); - if (control && control->isPanel()) + if (getCurrentPanel()) { - 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); + getCurrentPanel()->setFocus(TRUE); } } - 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) + if (!gFocusMgr.childHasKeyboardFocus(getCurrentPanel())) { - case START: - // insert the new tab in the front of the list - mTabList.insert(mTabList.begin() + mLockedTabCount, tuple); - break; - case LEFT_OF_CURRENT: - // insert the new tab before the current tab (but not before mLockedTabCount) + // if child has focus, but not the current panel, focus is on a button + if (mIsVertical) { - tuple_list_t::iterator current_iter = mTabList.begin() + llmax(mLockedTabCount, mCurrentTabIdx); - mTabList.insert(current_iter, tuple); + switch(key) + { + case KEY_UP: + selectPrevTab(); + handled = TRUE; + break; + case KEY_DOWN: + selectNextTab(); + handled = TRUE; + break; + case KEY_LEFT: + handled = TRUE; + break; + case KEY_RIGHT: + if (getTabPosition() == LEFT && getCurrentPanel()) + { + getCurrentPanel()->setFocus(TRUE); + } + handled = TRUE; + break; + default: + break; + } } - break; - - case RIGHT_OF_CURRENT: - // insert the new tab after the current tab (but not before mLockedTabCount) + else { - tuple_list_t::iterator current_iter = mTabList.begin() + llmax(mLockedTabCount, mCurrentTabIdx + 1); - mTabList.insert(current_iter, tuple); + 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; + } } - break; - case END: - default: - mTabList.push_back( tuple ); } + return handled; } - -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), - mJumpLeftArrowBtn(NULL), - mRightArrowBtn(NULL), - mJumpRightArrowBtn(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), - mJumpLeftArrowBtn(NULL), - mRightArrowBtn(NULL), - mJumpRightArrowBtn(NULL), - mRightTabBtnOffset(0), - mMinTabWidth(TABCNTR_TAB_MIN_WIDTH), - mMaxTabWidth(TABCNTR_TAB_MAX_WIDTH), - mTotalTabWidth(0) +// virtual +LLXMLNodePtr LLTabContainer::getXML(bool save_children) const { - initButtons( ); + LLXMLNodePtr node = LLPanel::getXML(); + node->createChild("tab_position", TRUE)->setStringValue((getTabPosition() == TOP ? "top" : "bottom")); + return node; } -void LLTabContainer::initButtons() +// virtual +BOOL LLTabContainer::handleDragAndDrop(S32 x, S32 y, MASK mask, BOOL drop, EDragAndDropType type, void* cargo_data, EAcceptance *accept, LLString &tooltip) { - // 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 + BOOL has_scroll_arrows = (getMaxScrollPos() > 0); - // tabs on bottom reserve room for resize handle (just in case) - if (mTabPosition == BOTTOM) + if( mDragAndDropDelayTimer.getElapsedTimeF32() > SCROLL_DELAY_TIME ) { - 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+TABCNTR_ARROW_BTN_SIZE, btn_top + arrow_fudge, TABCNTR_ARROW_BTN_SIZE, TABCNTR_ARROW_BTN_SIZE ); - - LLRect jump_left_arrow_btn_rect; - jump_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 - TABCNTR_ARROW_BTN_SIZE, - btn_top + arrow_fudge, - TABCNTR_ARROW_BTN_SIZE, TABCNTR_ARROW_BTN_SIZE ); - - - LLRect jump_right_arrow_btn_rect; - jump_right_arrow_btn_rect.setLeftTopAndSize( mRect.getWidth() - mRightTabBtnOffset - right_pad, - btn_top + arrow_fudge, - TABCNTR_ARROW_BTN_SIZE, TABCNTR_ARROW_BTN_SIZE ); - - out_id = "UIImgBtnJumpLeftOutUUID"; - in_id = "UIImgBtnJumpLeftInUUID"; - mJumpLeftArrowBtn = new LLButton( - "Jump Left Arrow", jump_left_arrow_btn_rect, - out_id, in_id, "", - &LLTabContainer::onJumpFirstBtn, this, LLFontGL::sSansSerif ); - mJumpLeftArrowBtn->setFollowsLeft(); - mJumpLeftArrowBtn->setSaveToXML(false); - mJumpLeftArrowBtn->setTabStop(FALSE); - addChild(mJumpLeftArrowBtn); + if (has_scroll_arrows) + { + if (mJumpPrevArrowBtn->getRect().pointInRect(x, y)) + { + S32 local_x = x - mJumpPrevArrowBtn->getRect().mLeft; + S32 local_y = y - mJumpPrevArrowBtn->getRect().mBottom; + mJumpPrevArrowBtn->handleHover(local_x, local_y, mask); + } + if (mJumpNextArrowBtn->getRect().pointInRect(x, y)) + { + S32 local_x = x - mJumpNextArrowBtn->getRect().mLeft; + S32 local_y = y - mJumpNextArrowBtn->getRect().mBottom; + mJumpNextArrowBtn->handleHover(local_x, local_y, mask); + } + if (mPrevArrowBtn->getRect().pointInRect(x, y)) + { + S32 local_x = x - mPrevArrowBtn->getRect().mLeft; + S32 local_y = y - mPrevArrowBtn->getRect().mBottom; + mPrevArrowBtn->handleHover(local_x, local_y, mask); + } + else if (mNextArrowBtn->getRect().pointInRect(x, y)) + { + S32 local_x = x - mNextArrowBtn->getRect().mLeft; + S32 local_y = y - mNextArrowBtn->getRect().mBottom; + mNextArrowBtn->handleHover(local_x, local_y, mask); + } + } - 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 = "UIImgBtnJumpRightOutUUID"; - in_id = "UIImgBtnJumpRightInUUID"; - mJumpRightArrowBtn = new LLButton( - "Jump Right Arrow", jump_right_arrow_btn_rect, - out_id, in_id, "", - &LLTabContainer::onJumpLastBtn, this, - LLFontGL::sSansSerif); - mJumpRightArrowBtn->setFollowsRight(); - mJumpRightArrowBtn->setSaveToXML(false); - mJumpRightArrowBtn->setTabStop(FALSE); - addChild(mJumpRightArrowBtn); - - 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(); - mJumpLeftArrowBtn->setFollowsTop(); - mJumpRightArrowBtn->setFollowsTop(); - } - else - { - mRightArrowBtn->setFollowsBottom(); - mLeftArrowBtn->setFollowsBottom(); - mJumpLeftArrowBtn->setFollowsBottom(); - mJumpRightArrowBtn->setFollowsBottom(); + 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(); + mDragAndDropDelayTimer.stop(); + } + } } - // set default tab group to be panel contents - mDefaultTabGroup = 1; -} - -LLTabContainer::~LLTabContainer() -{ + return LLView::handleDragAndDrop(x, y, mask, drop, type, cargo_data, accept, tooltip); } void LLTabContainer::addTabPanel(LLPanel* child, @@ -911,35 +683,49 @@ void LLTabContainer::addTabPanel(LLPanel* child, // already a child of mine return; } - const LLFontGL* font = gResMgr->getRes( LLFONT_SANSSERIF_SMALL ); + const LLFontGL* font = gResMgr->getRes( mIsVertical ? LLFONT_SANSSERIF : 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); - + S32 button_width = mMinTabWidth; + if (!mIsVertical) + { + 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 ) + if( getTabPosition() == LLTabContainer::TOP ) { - tab_panel_top = mRect.getHeight() - mTopBorderHeight - (TABCNTR_TAB_HEIGHT - TABCNTR_BUTTON_PANEL_OVERLAP); + S32 tab_height = mIsVertical ? BTN_HEIGHT : TABCNTR_TAB_HEIGHT; + tab_panel_top = getRect().getHeight() - getTopBorderHeight() - (tab_height - TABCNTR_BUTTON_PANEL_OVERLAP); tab_panel_bottom = LLPANEL_BORDER_WIDTH; } else { - tab_panel_top = mRect.getHeight() - mTopBorderHeight; + tab_panel_top = getRect().getHeight() - getTopBorderHeight(); 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 ); - + LLRect tab_panel_rect; + if (mIsVertical) + { + tab_panel_rect = LLRect(mMinTabWidth + (LLPANEL_BORDER_WIDTH * 2) + TABCNTRV_PAD, + getRect().getHeight() - LLPANEL_BORDER_WIDTH, + getRect().getWidth() - LLPANEL_BORDER_WIDTH, + LLPANEL_BORDER_WIDTH); + } + else + { + tab_panel_rect = LLRect(LLPANEL_BORDER_WIDTH, + tab_panel_top, + getRect().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 ); @@ -956,9 +742,16 @@ void LLTabContainer::addTabPanel(LLPanel* child, LLString tab_selected_img; S32 tab_fudge = 1; // To make new tab art look better, nudge buttons up 1 pel - if( LLTabContainer::TOP == mTabPosition ) + if (mIsVertical) { - btn_rect.setLeftTopAndSize( 0, mRect.getHeight() - mTopBorderHeight + tab_fudge, button_width, TABCNTR_TAB_HEIGHT ); + btn_rect.setLeftTopAndSize(TABCNTRV_PAD + LLPANEL_BORDER_WIDTH + 2, // JC - Fudge factor + (getRect().getHeight() - getTopBorderHeight() - LLPANEL_BORDER_WIDTH - 1) - ((BTN_HEIGHT + TABCNTRV_PAD) * getTabCount()), + mMinTabWidth, + BTN_HEIGHT); + } + else if( getTabPosition() == LLTabContainer::TOP ) + { + btn_rect.setLeftTopAndSize( 0, getRect().getHeight() - getTopBorderHeight() + tab_fudge, button_width, TABCNTR_TAB_HEIGHT ); tab_img = "tab_top_blue.tga"; tab_selected_img = "tab_top_selected_blue.tga"; } @@ -969,185 +762,377 @@ void LLTabContainer::addTabPanel(LLPanel* child, tab_selected_img = "tab_bottom_selected_blue.tga"; } + LLTextBox* textbox = NULL; + LLButton* btn = NULL; + if (placeholder) { - // *FIX: 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); + textbox = new LLTextBox(trimmed_label, btn_rect, trimmed_label, font); + + btn = new LLButton("", LLRect(0,0,0,0)); } else { - LLString tooltip = trimmed_label; - tooltip += "\nAlt-Left arrow for previous tab"; - tooltip += "\nAlt-Right arrow for next tab"; - - LLButton* btn = new LLButton( - LLString(child->getName()) + " tab", - btn_rect, - "", "", "", - &LLTabContainer::onTabBtn, NULL, // set userdata below - font, - trimmed_label, trimmed_label ); - btn->setSaveToXML(false); - btn->setVisible( FALSE ); - btn->setToolTip( tooltip ); - btn->setScaleImage(TRUE); - btn->setImages(tab_img, tab_selected_img); - - // 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 ) + if (mIsVertical) { - btn->setFollowsTop(); + btn = new LLButton("vert tab button", + btn_rect, + "", + "", + "", + &LLTabContainer::onTabBtn, NULL, + font, + trimmed_label, trimmed_label); + btn->setImages("tab_left.tga", "tab_left_selected.tga"); + btn->setScaleImage(TRUE); + btn->setHAlign(LLFontGL::LEFT); + btn->setFollows(FOLLOWS_TOP | FOLLOWS_LEFT); + btn->setTabStop(FALSE); + if (indent) + { + btn->setLeftHPad(indent); + } } else { - btn->setFollowsBottom(); + LLString tooltip = trimmed_label; + tooltip += "\nAlt-Left arrow for previous tab"; + tooltip += "\nAlt-Right arrow for next tab"; + + btn = new LLButton(LLString(child->getName()) + " tab", + btn_rect, + "", "", "", + &LLTabContainer::onTabBtn, NULL, // set userdata below + font, + trimmed_label, trimmed_label ); + btn->setVisible( FALSE ); + btn->setToolTip( tooltip ); + btn->setScaleImage(TRUE); + btn->setImages(tab_img, tab_selected_img); + + // 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( getTabPosition() == TOP ) + { + btn->setFollowsTop(); + } + else + { + btn->setFollowsBottom(); + } } + } + + LLTabTuple* tuple = new LLTabTuple( this, child, btn, on_tab_clicked, userdata, textbox ); + insertTuple( tuple, insertion_point ); - LLTabTuple* tuple = new LLTabTuple( this, child, btn, on_tab_clicked, userdata ); + if (textbox) + { + textbox->setSaveToXML(false); + addChild( textbox, 0 ); + } + if (btn) + { + btn->setSaveToXML(false); btn->setCallbackUserData( tuple ); addChild( btn, 0 ); - addChild( child, 1 ); - insertTuple(tuple, insertion_point); } - - updateMaxScrollPos(); - + if (child) + { + addChild(child, 1); + } + if( select ) { selectLastTab(); } + + updateMaxScrollPos(); +} + +void LLTabContainer::addPlaceholder(LLPanel* child, const LLString& label) +{ + addTabPanel(child, label, FALSE, NULL, NULL, 0, TRUE); } void LLTabContainer::removeTabPanel(LLPanel* child) { - // Adjust the total tab width. - for(tuple_list_t::iterator iter = mTabList.begin(); iter != mTabList.end(); ++iter) + if (mIsVertical) + { + // Fix-up button sizes + S32 tab_count = 0; + for(tuple_list_t::iterator iter = mTabList.begin(); iter != mTabList.end(); ++iter) + { + LLTabTuple* tuple = *iter; + LLRect rect; + rect.setLeftTopAndSize(TABCNTRV_PAD + LLPANEL_BORDER_WIDTH + 2, // JC - Fudge factor + (getRect().getHeight() - LLPANEL_BORDER_WIDTH - 1) - ((BTN_HEIGHT + TABCNTRV_PAD) * (tab_count)), + mMinTabWidth, + BTN_HEIGHT); + if (tuple->mPlaceholderText) + { + tuple->mPlaceholderText->setRect(rect); + } + else + { + tuple->mButton->setRect(rect); + } + tab_count++; + } + } + else + { + // 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; + } + } + } + + BOOL has_focus = gFocusMgr.childHasKeyboardFocus(this); + + // If the tab being deleted is the selected one, select a different tab. + for(std::vector::iterator iter = mTabList.begin(); iter != mTabList.end(); ++iter) { LLTabTuple* tuple = *iter; if( tuple->mTabPanel == child ) { - mTotalTabWidth -= tuple->mButton->getRect().getWidth(); + removeChild( tuple->mButton ); + delete tuple->mButton; + + removeChild( tuple->mTabPanel ); +// delete tuple->mTabPanel; + + mTabList.erase( iter ); + delete tuple; + break; } } - LLTabContainerCommon::removeTabPanel(child); + // make sure we don't have more locked tabs than we have tabs + mLockedTabCount = llmin(getTabCount(), mLockedTabCount); + + 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 LLTabContainer::setPanelTitle(S32 index, const LLString& title) +void LLTabContainer::lockTabs(S32 num_tabs) { - if (index >= 0 && index < (S32)mTabList.size()) + // count current tabs or use supplied value and ensure no new tabs get + // inserted between them + mLockedTabCount = num_tabs > 0 ? llmin(getTabCount(), num_tabs) : getTabCount(); +} + +void LLTabContainer::unlockTabs() +{ + mLockedTabCount = 0; +} + +void LLTabContainer::enableTabButton(S32 which, BOOL enable) +{ + if (which >= 0 && which < (S32)mTabList.size()) { - LLTabTuple* tuple = mTabList[index]; - LLButton* tab_button = tuple->mButton; - const LLFontGL* fontp = gResMgr->getRes( LLFONT_SANSSERIF_SMALL ); - mTotalTabWidth -= tab_button->getRect().getWidth(); - tab_button->reshape(llclamp(fontp->getWidth(title) + TAB_PADDING + tuple->mPadding, mMinTabWidth, mMaxTabWidth), tab_button->getRect().getHeight()); - mTotalTabWidth += tab_button->getRect().getWidth(); - tab_button->setLabelSelected(title); - tab_button->setLabelUnselected(title); + mTabList[which]->mButton->setEnabled(enable); } - updateMaxScrollPos(); } +void LLTabContainer::deleteAllTabs() +{ + // Remove all the tab buttons and delete them. Also, unlink all the child panels. + for(std::vector::iterator iter = mTabList.begin(); iter != mTabList.end(); ++iter) + { + LLTabTuple* tuple = *iter; -void LLTabContainer::updateMaxScrollPos() + 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* LLTabContainer::getCurrentPanel() +{ + if (mCurrentTabIdx >= 0 && mCurrentTabIdx < (S32) mTabList.size()) + { + return mTabList[mCurrentTabIdx]->mTabPanel; + } + return NULL; +} + +S32 LLTabContainer::getCurrentPanelIndex() +{ + return mCurrentTabIdx; +} + +S32 LLTabContainer::getTabCount() { - S32 tab_space = 0; - S32 available_space = 0; - tab_space = mTotalTabWidth; - available_space = mRect.getWidth() - mRightTabBtnOffset - 2 * (LLPANEL_BORDER_WIDTH + TABCNTR_TAB_H_PAD); + return mTabList.size(); +} - if( tab_space > available_space ) +LLPanel* LLTabContainer::getPanelByIndex(S32 index) +{ + if (index >= 0 && index < (S32)mTabList.size()) { - S32 available_width_with_arrows = mRect.getWidth() - mRightTabBtnOffset - 2 * (LLPANEL_BORDER_WIDTH + TABCNTR_ARROW_BTN_SIZE + TABCNTR_ARROW_BTN_SIZE + 1); - // subtract off reserved portion on left - available_width_with_arrows -= TABCNTR_TAB_PARTIAL_WIDTH; + return mTabList[index]->mTabPanel; + } + return NULL; +} - S32 running_tab_width = 0; - mMaxScrollPos = mTabList.size(); - for(tuple_list_t::reverse_iterator tab_it = mTabList.rbegin(); tab_it != mTabList.rend(); ++tab_it) +S32 LLTabContainer::getIndexForPanel(LLPanel* panel) +{ + for (S32 index = 0; index < (S32)mTabList.size(); index++) + { + if (mTabList[index]->mTabPanel == panel) { - running_tab_width += (*tab_it)->mButton->getRect().getWidth(); - if (running_tab_width > available_width_with_arrows) - { - break; - } - mMaxScrollPos--; + return index; } - // 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) + return -1; +} + +S32 LLTabContainer::getPanelIndexByTitle(const LLString& title) +{ + for (S32 index = 0 ; index < (S32)mTabList.size(); index++) { - mScrollPos = mMaxScrollPos; + if (title == mTabList[index]->mButton->getLabelSelected()) + { + return index; + } } + return -1; } -void LLTabContainer::commitHoveredButton(S32 x, S32 y) +LLPanel *LLTabContainer::getPanelByName(const LLString& name) { - if (hasMouseCapture()) + for (S32 index = 0 ; index < (S32)mTabList.size(); index++) { - for(tuple_list_t::iterator iter = mTabList.begin(); iter != mTabList.end(); ++iter) + LLPanel *panel = mTabList[index]->mTabPanel; + if (name == panel->getName()) { - 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 panel; } } -} - -void LLTabContainer::setMinTabWidth(S32 width) -{ - mMinTabWidth = width; -} - -void LLTabContainer::setMaxTabWidth(S32 width) -{ - mMaxTabWidth = width; -} - -S32 LLTabContainer::getMinTabWidth() const -{ - return mMinTabWidth; -} + return NULL; +} + +// Change the name of the button for the current tab. +void LLTabContainer::setCurrentTabName(const LLString& name) +{ + // Might not have a tab selected + if (mCurrentTabIdx < 0) return; + + mTabList[mCurrentTabIdx]->mButton->setLabelSelected(name); + mTabList[mCurrentTabIdx]->mButton->setLabelUnselected(name); +} + +void LLTabContainer::selectFirstTab() +{ + selectTab( 0 ); +} + + +void LLTabContainer::selectLastTab() +{ + selectTab( mTabList.size()-1 ); +} + +void LLTabContainer::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 LLTabContainer::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); + } +} -S32 LLTabContainer::getMaxTabWidth() const +BOOL LLTabContainer::selectTabPanel(LLPanel* child) { - return mMaxTabWidth; + 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 LLTabContainer::selectTab(S32 which) { - if (which >= (S32)mTabList.size()) return FALSE; + if (which >= getTabCount()) return FALSE; if (which < 0) return FALSE; //if( gFocusMgr.childHasKeyboardFocus( this ) ) @@ -1155,15 +1140,16 @@ BOOL LLTabContainer::selectTab(S32 which) // gFocusMgr.setKeyboardFocus( NULL ); //} - LLTabTuple* selected_tuple = mTabList[which]; + LLTabTuple* selected_tuple = getTab(which); if (!selected_tuple) { return FALSE; } - if (mTabList[which]->mButton->getEnabled()) + BOOL is_visible = FALSE; + if (getTab(which)->mButton->getEnabled()) { - mCurrentTabIdx = which; + setCurrentPanelIndex(which); S32 i = 0; for(tuple_list_t::iterator iter = mTabList.begin(); iter != mTabList.end(); ++iter) @@ -1176,35 +1162,52 @@ BOOL LLTabContainer::selectTab(S32 which) // RN: this limits tab-stops to active button only, which would require arrow keys to switch tabs tuple->mButton->setTabStop( is_selected ); - if( is_selected && mMaxScrollPos > 0) + if( is_selected && (mIsVertical || (getMaxScrollPos() > 0))) { // Make sure selected tab is within scroll region - if( i < mScrollPos ) + if (mIsVertical) { - mScrollPos = i; + S32 num_visible = getTabCount() - getMaxScrollPos(); + if( i >= getScrollPos() && i <= getScrollPos() + num_visible) + { + setCurrentPanelIndex(which); + is_visible = TRUE; + } + else + { + is_visible = FALSE; + } } else { - S32 available_width_with_arrows = mRect.getWidth() - mRightTabBtnOffset - 2 * (LLPANEL_BORDER_WIDTH + TABCNTR_ARROW_BTN_SIZE + 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) + if( i < getScrollPos() ) + { + setScrollPos(i); + } + else { - while (j >= 0) + S32 available_width_with_arrows = getRect().getWidth() - mRightTabBtnOffset - 2 * (LLPANEL_BORDER_WIDTH + TABCNTR_ARROW_BTN_SIZE + 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) { - LLTabTuple* other_tuple = mTabList[j]; - running_tab_width += other_tuple->mButton->getRect().getWidth(); - if (running_tab_width > available_width_with_arrows) + while (j >= 0) { - break; + LLTabTuple* other_tuple = getTab(j); + running_tab_width += other_tuple->mButton->getRect().getWidth(); + if (running_tab_width > available_width_with_arrows) + { + break; + } + j--; } - j--; + min_scroll_pos = j + 1; } - min_scroll_pos = j + 1; + setScrollPos(llclamp(getScrollPos(), min_scroll_pos, i)); + setScrollPos(llmin(getScrollPos(), getMaxScrollPos())); } - mScrollPos = llclamp(mScrollPos, min_scroll_pos, i); - mScrollPos = llmin(mScrollPos, mMaxScrollPos); + is_visible = TRUE; } } i++; @@ -1213,463 +1216,595 @@ BOOL LLTabContainer::selectTab(S32 which) { selected_tuple->mOnChangeCallback( selected_tuple->mUserData, false ); } - return TRUE; } - else + if (mIsVertical && getCurrentPanelIndex() >= 0) { - return FALSE; + LLTabTuple* tuple = getTab(getCurrentPanelIndex()); + tuple->mTabPanel->setVisible( TRUE ); + tuple->mButton->setToggleState( TRUE ); } + return is_visible; } -void LLTabContainer::draw() +BOOL LLTabContainer::selectTabByName(const LLString& name) { - S32 target_pixel_scroll = 0; - S32 cur_scroll_pos = mScrollPos; - if (cur_scroll_pos > 0) + LLPanel* panel = getPanelByName(name); + if (!panel) { - S32 available_width_with_arrows = mRect.getWidth() - mRightTabBtnOffset - 2 * (LLPANEL_BORDER_WIDTH + TABCNTR_ARROW_BTN_SIZE + 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--; - } + llwarns << "LLTabContainer::selectTabByName(" + << name << ") failed" << llendl; + return FALSE; + } - // 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); + BOOL result = selectTabPanel(panel); + return result; +} + +BOOL LLTabContainer::getTabPanelFlashing(LLPanel *child) +{ + LLTabTuple* tuple = getTabByPanel(child); + if( tuple ) + { + return tuple->mButton->getFlashing(); } + return FALSE; +} - mScrollPosPixels = (S32)lerp((F32)mScrollPosPixels, (F32)target_pixel_scroll, LLCriticalDamp::getInterpolant(0.08f)); - if( getVisible() ) +void LLTabContainer::setTabPanelFlashing(LLPanel* child, BOOL state ) +{ + LLTabTuple* tuple = getTabByPanel(child); + if( tuple ) { - BOOL has_scroll_arrows = (mMaxScrollPos > 0) || (mScrollPosPixels > 0); - mJumpLeftArrowBtn->setVisible( has_scroll_arrows ); - mJumpRightArrowBtn->setVisible( has_scroll_arrows ); - mLeftArrowBtn->setVisible( has_scroll_arrows ); - mRightArrowBtn->setVisible( has_scroll_arrows ); + tuple->mButton->setFlashing( state ); + } +} - // Set the leftmost position of the tab buttons. - S32 left = LLPANEL_BORDER_WIDTH + (has_scroll_arrows ? (TABCNTR_ARROW_BTN_SIZE * 2) : TABCNTR_TAB_H_PAD); - left -= mScrollPosPixels; +void LLTabContainer::setTabImage(LLPanel* child, std::string image_name, const LLColor4& color) +{ + LLTabTuple* tuple = getTabByPanel(child); + if( tuple ) + { + tuple->mButton->setImageOverlay(image_name, LLFontGL::RIGHT, color); - // Hide all the buttons - for(tuple_list_t::iterator iter = mTabList.begin(); iter != mTabList.end(); ++iter) + if (!mIsVertical) { - LLTabTuple* tuple = *iter; - tuple->mButton->setVisible( FALSE ); - } + const LLFontGL* fontp = gResMgr->getRes( LLFONT_SANSSERIF_SMALL ); + // remove current width from total tab strip width + mTotalTabWidth -= tuple->mButton->getRect().getWidth(); - LLPanel::draw(); + S32 image_overlay_width = tuple->mButton->getImageOverlay().notNull() ? + tuple->mButton->getImageOverlay()->getImage()->getWidth(0) : + 0; - // if tabs are hidden, don't draw them and leave them in the invisible state - if (!mTabsHidden) - { - // Show all the buttons - for(tuple_list_t::iterator iter = mTabList.begin(); iter != mTabList.end(); ++iter) - { - LLTabTuple* tuple = *iter; - tuple->mButton->setVisible( TRUE ); - } + tuple->mPadding = image_overlay_width; - // Draw some of the buttons... - LLRect clip_rect = getLocalRect(); - if (has_scroll_arrows) - { - // ...but clip them. - clip_rect.mLeft = mLeftArrowBtn->getRect().mRight; - clip_rect.mRight = mRightArrowBtn->getRect().mLeft; - } - LLLocalClipRect clip(clip_rect); + tuple->mButton->setRightHPad(6); + tuple->mButton->reshape(llclamp(fontp->getWidth(tuple->mButton->getLabelSelected()) + TAB_PADDING + tuple->mPadding, mMinTabWidth, mMaxTabWidth), + tuple->mButton->getRect().getHeight()); + // add back in button width to total tab strip width + mTotalTabWidth += tuple->mButton->getRect().getWidth(); - 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; + // tabs have changed size, might need to scroll to see current tab + updateMaxScrollPos(); + } + } +} - tuple->mButton->translate( left - tuple->mButton->getRect().mLeft, 0 ); - left += tuple->mButton->getRect().getWidth(); +void LLTabContainer::setTitle(const LLString& title) +{ + if (mTitleBox) + { + mTitleBox->setText( title ); + } +} - if( idx < mScrollPos ) - { - if( tuple->mButton->getFlashing() ) - { - mLeftArrowBtn->setFlashing( TRUE ); - } - } - else - if( max_scroll_visible < idx ) - { - if( tuple->mButton->getFlashing() ) - { - mRightArrowBtn->setFlashing( TRUE ); - } - } +const LLString LLTabContainer::getPanelTitle(S32 index) +{ + if (index >= 0 && index < (S32)mTabList.size()) + { + LLButton* tab_button = mTabList[index]->mButton; + return tab_button->getLabelSelected(); + } + return LLString::null; +} - LLUI::pushMatrix(); - { - LLUI::translate((F32)tuple->mButton->getRect().mLeft, (F32)tuple->mButton->getRect().mBottom, 0.f); - tuple->mButton->draw(); - } - LLUI::popMatrix(); - - idx++; - } - } +void LLTabContainer::setTopBorderHeight(S32 height) +{ + mTopBorderHeight = height; +} + +S32 LLTabContainer::getTopBorderHeight() const +{ + return mTopBorderHeight; +} - mLeftArrowBtn->setFlashing(FALSE); - mRightArrowBtn->setFlashing(FALSE); +void LLTabContainer::setTabChangeCallback(LLPanel* tab, void (*on_tab_clicked)(void*, bool)) +{ + LLTabTuple* tuplep = getTabByPanel(tab); + if (tuplep) + { + tuplep->mOnChangeCallback = on_tab_clicked; } } +void LLTabContainer::setTabUserData(LLPanel* tab, void* userdata) +{ + LLTabTuple* tuplep = getTabByPanel(tab); + if (tuplep) + { + tuplep->mUserData = userdata; + } +} void LLTabContainer::setRightTabBtnOffset(S32 offset) { - mRightArrowBtn->translate( -offset - mRightTabBtnOffset, 0 ); + mNextArrowBtn->translate( -offset - mRightTabBtnOffset, 0 ); mRightTabBtnOffset = offset; updateMaxScrollPos(); } -BOOL LLTabContainer::handleMouseDown( S32 x, S32 y, MASK mask ) +void LLTabContainer::setPanelTitle(S32 index, const LLString& title) { - BOOL handled = FALSE; - BOOL has_scroll_arrows = (mMaxScrollPos > 0); + if (index >= 0 && index < getTabCount()) + { + LLTabTuple* tuple = getTab(index); + LLButton* tab_button = tuple->mButton; + const LLFontGL* fontp = gResMgr->getRes( LLFONT_SANSSERIF_SMALL ); + mTotalTabWidth -= tab_button->getRect().getWidth(); + tab_button->reshape(llclamp(fontp->getWidth(title) + TAB_PADDING + tuple->mPadding, mMinTabWidth, mMaxTabWidth), tab_button->getRect().getHeight()); + mTotalTabWidth += tab_button->getRect().getWidth(); + tab_button->setLabelSelected(title); + tab_button->setLabelUnselected(title); + } + updateMaxScrollPos(); +} - if (has_scroll_arrows) - { - if (mJumpLeftArrowBtn->getRect().pointInRect(x, y)) - { - S32 local_x = x - mJumpLeftArrowBtn->getRect().mLeft; - S32 local_y = y - mJumpLeftArrowBtn->getRect().mBottom; - handled = mJumpLeftArrowBtn->handleMouseDown(local_x, local_y, mask); - } - if (mJumpRightArrowBtn->getRect().pointInRect(x, y)) - { - S32 local_x = x - mJumpRightArrowBtn->getRect().mLeft; - S32 local_y = y - mJumpRightArrowBtn->getRect().mBottom; - handled = mJumpRightArrowBtn->handleMouseDown(local_x, local_y, mask); - } - 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); - } + +// static +void LLTabContainer::onTabBtn( void* userdata ) +{ + LLTabTuple* tuple = (LLTabTuple*) userdata; + LLTabContainer* self = tuple->mTabContainer; + self->selectTabPanel( tuple->mTabPanel ); + + if( tuple->mOnChangeCallback ) + { + tuple->mOnChangeCallback( tuple->mUserData, true ); } - if (!handled) + + tuple->mTabPanel->setFocus(TRUE); +} + +// static +void LLTabContainer::onCloseBtn( void* userdata ) +{ + LLTabContainer* self = (LLTabContainer*) userdata; + if( self->mCloseCallback ) { - handled = LLPanel::handleMouseDown( x, y, mask ); + self->mCloseCallback( self->mCallbackUserdata ); } +} - if (mTabList.size() > 0) +// static +void LLTabContainer::onNextBtn( void* userdata ) +{ + // Scroll tabs to the left + LLTabContainer* self = (LLTabContainer*) userdata; + if (!self->mScrolled) { - LLTabTuple* firsttuple = mTabList[0]; - LLRect tab_rect(has_scroll_arrows ? mLeftArrowBtn->getRect().mRight : mJumpLeftArrowBtn->getRect().mLeft, - firsttuple->mButton->getRect().mTop, - has_scroll_arrows ? mRightArrowBtn->getRect().mLeft : mJumpRightArrowBtn->getRect().mRight, - firsttuple->mButton->getRect().mBottom ); - if( tab_rect.pointInRect( x, y ) ) - { - LLButton* tab_button = mTabList[getCurrentPanelIndex()]->mButton; - gFocusMgr.setMouseCapture(this); - gFocusMgr.setKeyboardFocus(tab_button); - } + self->scrollNext(); } - return handled; + self->mScrolled = FALSE; } -BOOL LLTabContainer::handleHover( S32 x, S32 y, MASK mask ) +// static +void LLTabContainer::onNextBtnHeld( void* userdata ) { - BOOL handled = FALSE; - BOOL has_scroll_arrows = (mMaxScrollPos > 0); + LLTabContainer* self = (LLTabContainer*) userdata; + if (self->mScrollTimer.getElapsedTimeF32() > SCROLL_STEP_TIME) + { + self->mScrollTimer.reset(); + self->scrollNext(); + self->mScrolled = TRUE; + } +} - if (has_scroll_arrows) - { - if (mJumpLeftArrowBtn->getRect().pointInRect(x, y)) - { - S32 local_x = x - mJumpLeftArrowBtn->getRect().mLeft; - S32 local_y = y - mJumpLeftArrowBtn->getRect().mBottom; - handled = mJumpLeftArrowBtn->handleHover(local_x, local_y, mask); - } - if (mJumpRightArrowBtn->getRect().pointInRect(x, y)) - { - S32 local_x = x - mJumpRightArrowBtn->getRect().mLeft; - S32 local_y = y - mJumpRightArrowBtn->getRect().mBottom; - handled = mJumpRightArrowBtn->handleHover(local_x, local_y, mask); - } - 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); - } +// static +void LLTabContainer::onPrevBtn( void* userdata ) +{ + LLTabContainer* self = (LLTabContainer*) userdata; + if (!self->mScrolled) + { + self->scrollPrev(); } - if (!handled) + self->mScrolled = FALSE; +} + +// static +void LLTabContainer::onJumpFirstBtn( void* userdata ) +{ + LLTabContainer* self = (LLTabContainer*) userdata; + self->mScrollPos = 0; +} + +// static +void LLTabContainer::onJumpLastBtn( void* userdata ) +{ + LLTabContainer* self = (LLTabContainer*) userdata; + self->mScrollPos = self->mMaxScrollPos; +} + +// static +void LLTabContainer::onPrevBtnHeld( void* userdata ) +{ + LLTabContainer* self = (LLTabContainer*) userdata; + if (self->mScrollTimer.getElapsedTimeF32() > SCROLL_STEP_TIME) { - handled = LLPanel::handleHover(x, y, mask); + self->mScrollTimer.reset(); + self->scrollPrev(); + self->mScrolled = TRUE; } - - commitHoveredButton(x, y); - return handled; } -BOOL LLTabContainer::handleMouseUp( S32 x, S32 y, MASK mask ) +// static +LLView* LLTabContainer::fromXML(LLXMLNodePtr node, LLView *parent, LLUICtrlFactory *factory) { - BOOL handled = FALSE; - BOOL has_scroll_arrows = (mMaxScrollPos > 0); + LLString name("tab_container"); + node->getAttributeString("name", name); - if (has_scroll_arrows) + // 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")) { - if (mJumpLeftArrowBtn->getRect().pointInRect(x, y)) - { - S32 local_x = x - mJumpLeftArrowBtn->getRect().mLeft; - S32 local_y = y - mJumpLeftArrowBtn->getRect().mBottom; - handled = mJumpLeftArrowBtn->handleMouseUp(local_x, local_y, mask); - } - if (mJumpRightArrowBtn->getRect().pointInRect(x, y)) + LLString tab_position_string; + node->getAttributeString("tab_position", tab_position_string); + LLString::toLower(tab_position_string); + + if ("top" == tab_position_string) { - S32 local_x = x - mJumpRightArrowBtn->getRect().mLeft; - S32 local_y = y - mJumpRightArrowBtn->getRect().mBottom; - handled = mJumpRightArrowBtn->handleMouseUp(local_x, local_y, mask); + tab_position = LLTabContainer::TOP; + is_vertical = false; } - if (mLeftArrowBtn->getRect().pointInRect(x, y)) + else if ("bottom" == tab_position_string) { - S32 local_x = x - mLeftArrowBtn->getRect().mLeft; - S32 local_y = y - mLeftArrowBtn->getRect().mBottom; - handled = mLeftArrowBtn->handleMouseUp(local_x, local_y, mask); + tab_position = LLTabContainer::BOTTOM; + is_vertical = false; } - else if (mRightArrowBtn->getRect().pointInRect(x, y)) + else if ("left" == tab_position_string) { - S32 local_x = x - mRightArrowBtn->getRect().mLeft; - S32 local_y = y - mRightArrowBtn->getRect().mBottom; - handled = mRightArrowBtn->handleMouseUp(local_x, local_y, mask); + is_vertical = true; } } - if (!handled) + BOOL border = FALSE; + node->getAttributeBOOL("border", border); + + LLTabContainer* tab_container = new LLTabContainer(name, LLRect::null, tab_position, border, is_vertical); + + S32 tab_min_width = tab_container->mMinTabWidth; + if (node->hasAttribute("tab_width")) { - handled = LLPanel::handleMouseUp( x, y, mask ); + node->getAttributeS32("tab_width", tab_min_width); + } + else if( node->hasAttribute("tab_min_width")) + { + node->getAttributeS32("tab_min_width", tab_min_width); } - commitHoveredButton(x, y); - LLPanel* cur_panel = getCurrentPanel(); - if (hasMouseCapture()) + S32 tab_max_width = tab_container->mMaxTabWidth; + if (node->hasAttribute("tab_max_width")) { - if (cur_panel) + node->getAttributeS32("tab_max_width", tab_max_width); + } + + tab_container->setMinTabWidth(tab_min_width); + tab_container->setMaxTabWidth(tab_max_width); + + BOOL hidden(tab_container->getTabsHidden()); + node->getAttributeBOOL("hide_tabs", hidden); + tab_container->setTabsHidden(hidden); + + 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()) { - if (!cur_panel->focusFirstItem(FALSE)) + LLPanel* panelp = (LLPanel*)control; + LLString label; + child->getAttributeString("label", label); + if (label.empty()) { - // 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); + label = panelp->getLabel(); } + BOOL placeholder = FALSE; + child->getAttributeBOOL("placeholder", placeholder); + tab_container->addTabPanel(panelp, label.c_str(), false, + NULL, NULL, 0, placeholder); } - gFocusMgr.setMouseCapture(NULL); } - return handled; + + tab_container->selectFirstTab(); + + tab_container->postBuild(); + + tab_container->initButtons(); // now that we have the correct rect + + return tab_container; } -BOOL LLTabContainer::handleToolTip( S32 x, S32 y, LLString& msg, LLRect* sticky_rect ) +// private + +void LLTabContainer::initButtons() { - BOOL handled = LLPanel::handleToolTip( x, y, msg, sticky_rect ); - if (!handled && mTabList.size() > 0) + // Hack: + if (getRect().getHeight() == 0 || mPrevArrowBtn) { - LLTabTuple* firsttuple = mTabList[0]; + return; // Don't have a rect yet or already got called + } + + LLString out_id; + LLString in_id; - BOOL has_scroll_arrows = (mMaxScrollPos > 0); - LLRect clip( - has_scroll_arrows ? mJumpLeftArrowBtn->getRect().mRight : mJumpLeftArrowBtn->getRect().mLeft, - firsttuple->mButton->getRect().mTop, - has_scroll_arrows ? mRightArrowBtn->getRect().mLeft : mRightArrowBtn->getRect().mRight, - 0 ); - if( clip.pointInRect( x, y ) ) + if (mIsVertical) + { + // Left and right scroll arrows (for when there are too many tabs to show all at once). + S32 btn_top = getRect().getHeight(); + S32 btn_top_lower = getRect().mBottom+TABCNTRV_ARROW_BTN_SIZE; + + LLRect up_arrow_btn_rect; + up_arrow_btn_rect.setLeftTopAndSize( mMinTabWidth/2 , btn_top, TABCNTRV_ARROW_BTN_SIZE, TABCNTRV_ARROW_BTN_SIZE ); + + LLRect down_arrow_btn_rect; + down_arrow_btn_rect.setLeftTopAndSize( mMinTabWidth/2 , btn_top_lower, TABCNTRV_ARROW_BTN_SIZE, TABCNTRV_ARROW_BTN_SIZE ); + + out_id = "UIImgBtnScrollUpOutUUID"; + in_id = "UIImgBtnScrollUpInUUID"; + mPrevArrowBtn = new LLButton("Up Arrow", up_arrow_btn_rect, + out_id, in_id, "", + &onPrevBtn, this, NULL ); + mPrevArrowBtn->setFollowsTop(); + mPrevArrowBtn->setFollowsLeft(); + + out_id = "UIImgBtnScrollDownOutUUID"; + in_id = "UIImgBtnScrollDownInUUID"; + mNextArrowBtn = new LLButton("Down Arrow", down_arrow_btn_rect, + out_id, in_id, "", + &onNextBtn, this, NULL ); + mNextArrowBtn->setFollowsBottom(); + mNextArrowBtn->setFollowsLeft(); + } + else // Horizontal + { + S32 arrow_fudge = 1; // match new art better + + // tabs on bottom reserve room for resize handle (just in case) + if (getTabPosition() == BOTTOM) { - 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; - } - } + mRightTabBtnOffset = RESIZE_HANDLE_WIDTH; } - for(tuple_list_t::iterator iter = mTabList.begin(); iter != mTabList.end(); ++iter) + // Left and right scroll arrows (for when there are too many tabs to show all at once). + S32 btn_top = (getTabPosition() == TOP ) ? getRect().getHeight() - getTopBorderHeight() : TABCNTR_ARROW_BTN_SIZE + 1; + + LLRect left_arrow_btn_rect; + left_arrow_btn_rect.setLeftTopAndSize( LLPANEL_BORDER_WIDTH+1+TABCNTR_ARROW_BTN_SIZE, btn_top + arrow_fudge, TABCNTR_ARROW_BTN_SIZE, TABCNTR_ARROW_BTN_SIZE ); + + LLRect jump_left_arrow_btn_rect; + jump_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( getRect().getWidth() - mRightTabBtnOffset - right_pad - TABCNTR_ARROW_BTN_SIZE, + btn_top + arrow_fudge, + TABCNTR_ARROW_BTN_SIZE, TABCNTR_ARROW_BTN_SIZE ); + + + LLRect jump_right_arrow_btn_rect; + jump_right_arrow_btn_rect.setLeftTopAndSize( getRect().getWidth() - mRightTabBtnOffset - right_pad, + btn_top + arrow_fudge, + TABCNTR_ARROW_BTN_SIZE, TABCNTR_ARROW_BTN_SIZE ); + + out_id = "UIImgBtnJumpLeftOutUUID"; + in_id = "UIImgBtnJumpLeftInUUID"; + mJumpPrevArrowBtn = new LLButton("Jump Left Arrow", jump_left_arrow_btn_rect, + out_id, in_id, "", + &LLTabContainer::onJumpFirstBtn, this, LLFontGL::sSansSerif ); + mJumpPrevArrowBtn->setFollowsLeft(); + + out_id = "UIImgBtnScrollLeftOutUUID"; + in_id = "UIImgBtnScrollLeftInUUID"; + mPrevArrowBtn = new LLButton("Left Arrow", left_arrow_btn_rect, + out_id, in_id, "", + &LLTabContainer::onPrevBtn, this, LLFontGL::sSansSerif ); + mPrevArrowBtn->setHeldDownCallback(onPrevBtnHeld); + mPrevArrowBtn->setFollowsLeft(); + + out_id = "UIImgBtnJumpRightOutUUID"; + in_id = "UIImgBtnJumpRightInUUID"; + mJumpNextArrowBtn = new LLButton("Jump Right Arrow", jump_right_arrow_btn_rect, + out_id, in_id, "", + &LLTabContainer::onJumpLastBtn, this, + LLFontGL::sSansSerif); + mJumpNextArrowBtn->setFollowsRight(); + + out_id = "UIImgBtnScrollRightOutUUID"; + in_id = "UIImgBtnScrollRightInUUID"; + mNextArrowBtn = new LLButton("Right Arrow", right_arrow_btn_rect, + out_id, in_id, "", + &LLTabContainer::onNextBtn, this, + LLFontGL::sSansSerif); + mNextArrowBtn->setFollowsRight(); + + if( getTabPosition() == TOP ) { - LLTabTuple* tuple = *iter; - tuple->mButton->setVisible( FALSE ); + mNextArrowBtn->setFollowsTop(); + mPrevArrowBtn->setFollowsTop(); + mJumpPrevArrowBtn->setFollowsTop(); + mJumpNextArrowBtn->setFollowsTop(); + } + else + { + mNextArrowBtn->setFollowsBottom(); + mPrevArrowBtn->setFollowsBottom(); + mJumpPrevArrowBtn->setFollowsBottom(); + mJumpNextArrowBtn->setFollowsBottom(); } } - return handled; -} -BOOL LLTabContainer::handleKeyHere(KEY key, MASK mask, BOOL called_from_parent) -{ - if (!getEnabled()) return FALSE; + mPrevArrowBtn->setHeldDownCallback(onPrevBtnHeld); + mPrevArrowBtn->setSaveToXML(false); + mPrevArrowBtn->setTabStop(FALSE); + addChild(mPrevArrowBtn); - if (!gFocusMgr.childHasKeyboardFocus(this)) return FALSE; + mNextArrowBtn->setHeldDownCallback(onNextBtnHeld); + mNextArrowBtn->setSaveToXML(false); + mNextArrowBtn->setTabStop(FALSE); + addChild(mNextArrowBtn); - BOOL handled = FALSE; - if (key == KEY_LEFT && mask == MASK_ALT) + if (mJumpPrevArrowBtn) { - selectPrevTab(); - handled = TRUE; + mJumpPrevArrowBtn->setSaveToXML(false); + mJumpPrevArrowBtn->setTabStop(FALSE); + addChild(mJumpPrevArrowBtn); } - else if (key == KEY_RIGHT && mask == MASK_ALT) + + if (mJumpNextArrowBtn) { - selectNextTab(); - handled = TRUE; + mJumpNextArrowBtn->setSaveToXML(false); + mJumpNextArrowBtn->setTabStop(FALSE); + addChild(mJumpNextArrowBtn); } + + // set default tab group to be panel contents + setDefaultTabGroup(1); +} - if (handled) +LLTabContainer::LLTabTuple* LLTabContainer::getTabByPanel(LLPanel* child) +{ + for(tuple_list_t::iterator iter = mTabList.begin(); iter != mTabList.end(); ++iter) { - if (getCurrentPanel()) + LLTabTuple* tuple = *iter; + if( tuple->mTabPanel == child ) { - getCurrentPanel()->setFocus(TRUE); + return tuple; } } + return NULL; +} - if (!gFocusMgr.childHasKeyboardFocus(getCurrentPanel())) +void LLTabContainer::insertTuple(LLTabTuple * tuple, eInsertionPoint insertion_point) +{ + switch(insertion_point) { - // if child has focus, but not the current panel, focus - // is on a button - switch(key) + case START: + // insert the new tab in the front of the list + mTabList.insert(mTabList.begin() + mLockedTabCount, tuple); + break; + case LEFT_OF_CURRENT: + // insert the new tab before the current tab (but not before mLockedTabCount) { - 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; + tuple_list_t::iterator current_iter = mTabList.begin() + llmax(mLockedTabCount, mCurrentTabIdx); + mTabList.insert(current_iter, tuple); + } + break; + + case RIGHT_OF_CURRENT: + // insert the new tab after the current tab (but not before mLockedTabCount) + { + tuple_list_t::iterator current_iter = mTabList.begin() + llmax(mLockedTabCount, mCurrentTabIdx + 1); + mTabList.insert(current_iter, tuple); } + break; + case END: + default: + mTabList.push_back( tuple ); } - 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) +void LLTabContainer::updateMaxScrollPos() { - BOOL has_scroll_arrows = (mMaxScrollPos > 0); - - if( mDragAndDropDelayTimer.getElapsedTimeF32() > SCROLL_DELAY_TIME ) + BOOL no_scroll = TRUE; + if (mIsVertical) { - - if (has_scroll_arrows) + S32 tab_total_height = (BTN_HEIGHT + TABCNTRV_PAD) * getTabCount(); + S32 available_height = getRect().getHeight() - getTopBorderHeight(); + if( tab_total_height > available_height ) { - if (mJumpLeftArrowBtn->getRect().pointInRect(x, y)) - { - S32 local_x = x - mJumpLeftArrowBtn->getRect().mLeft; - S32 local_y = y - mJumpLeftArrowBtn->getRect().mBottom; - mJumpLeftArrowBtn->handleHover(local_x, local_y, mask); - } - if (mJumpRightArrowBtn->getRect().pointInRect(x, y)) - { - S32 local_x = x - mJumpRightArrowBtn->getRect().mLeft; - S32 local_y = y - mJumpRightArrowBtn->getRect().mBottom; - mJumpRightArrowBtn->handleHover(local_x, local_y, mask); - } - 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); - } + S32 available_height_with_arrows = getRect().getHeight() - 2*(TABCNTRV_ARROW_BTN_SIZE + 3*TABCNTRV_PAD); + S32 additional_needed = tab_total_height - available_height_with_arrows; + setMaxScrollPos((S32) ceil(additional_needed / float(BTN_HEIGHT) ) ); + no_scroll = FALSE; } + } + else + { + S32 tab_space = 0; + S32 available_space = 0; + tab_space = mTotalTabWidth; + available_space = getRect().getWidth() - mRightTabBtnOffset - 2 * (LLPANEL_BORDER_WIDTH + TABCNTR_TAB_H_PAD); - for(tuple_list_t::iterator iter = mTabList.begin(); iter != mTabList.end(); ++iter) + if( tab_space > available_space ) { - 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()) + S32 available_width_with_arrows = getRect().getWidth() - mRightTabBtnOffset - 2 * (LLPANEL_BORDER_WIDTH + TABCNTR_ARROW_BTN_SIZE + TABCNTR_ARROW_BTN_SIZE + 1); + // subtract off reserved portion on left + available_width_with_arrows -= TABCNTR_TAB_PARTIAL_WIDTH; + + S32 running_tab_width = 0; + setMaxScrollPos(getTabCount()); + for(tuple_list_t::reverse_iterator tab_it = mTabList.rbegin(); tab_it != mTabList.rend(); ++tab_it) { - tuple->mButton->onCommit(); - mDragAndDropDelayTimer.stop(); + running_tab_width += (*tab_it)->mButton->getRect().getWidth(); + if (running_tab_width > available_width_with_arrows) + { + break; + } + setMaxScrollPos(getMaxScrollPos()-1); } + // in case last tab doesn't actually fit on screen, make it the last scrolling position + setMaxScrollPos(llmin(getMaxScrollPos(), getTabCount() - 1)); + no_scroll = FALSE; } } - - return LLView::handleDragAndDrop(x, y, mask, drop, type, cargo_data, accept, tooltip); + if (no_scroll) + { + setMaxScrollPos(0); + setScrollPos(0); + } + if (getScrollPos() > getMaxScrollPos()) + { + setScrollPos(getMaxScrollPos()); // maybe just enforce this via limits in setScrollPos instead? + } } -void LLTabContainer::setTabImage(LLPanel* child, std::string image_name, const LLColor4& color) +void LLTabContainer::commitHoveredButton(S32 x, S32 y) { - LLTabTuple* tuple = getTabByPanel(child); - if( tuple ) + if (hasMouseCapture()) { - tuple->mButton->setImageOverlay(image_name, LLFontGL::RIGHT, color); - - const LLFontGL* fontp = gResMgr->getRes( LLFONT_SANSSERIF_SMALL ); - // remove current width from total tab strip width - mTotalTabWidth -= tuple->mButton->getRect().getWidth(); - - S32 image_overlay_width = tuple->mButton->getImageOverlay().notNull() ? - tuple->mButton->getImageOverlay()->getImage()->getWidth(0) : - 0; - - tuple->mPadding = image_overlay_width; - - tuple->mButton->setRightHPad(6); - tuple->mButton->reshape(llclamp(fontp->getWidth(tuple->mButton->getLabelSelected()) + TAB_PADDING + tuple->mPadding, mMinTabWidth, mMaxTabWidth), - tuple->mButton->getRect().getHeight()); - // add back in button width to total tab strip width - mTotalTabWidth += tuple->mButton->getRect().getWidth(); - - // tabs have changed size, might need to scroll to see current tab - updateMaxScrollPos(); + 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(); + } + } } } + -- cgit v1.2.3