phoenix-firestorm/indra/llui/lltabcontainer.cpp

1579 lines
40 KiB
C++

/**
* @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;
}
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)
{
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),
mJumpLeftArrowBtn(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),
mRightArrowBtn(NULL),
mJumpLeftArrowBtn(NULL),
mJumpRightArrowBtn(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+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);
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();
}
// 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)
{
// *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);
}
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 + 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 (hasMouseCapture())
{
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 );
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 + 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 + 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);
mJumpLeftArrowBtn->setVisible( has_scroll_arrows );
mJumpRightArrowBtn->setVisible( has_scroll_arrows );
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 * 2) : 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 (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);
}
}
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 : 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, 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 (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);
}
}
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 (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))
{
S32 local_x = x - mJumpRightArrowBtn->getRect().mLeft;
S32 local_y = y - mJumpRightArrowBtn->getRect().mBottom;
handled = mJumpRightArrowBtn->handleMouseUp(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->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 (hasMouseCapture())
{
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);
}
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 ? 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 ) )
{
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 (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);
}
}
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);
}