PageRenderTime 151ms CodeModel.GetById 14ms app.highlight 124ms RepoModel.GetById 1ms app.codeStats 1ms

/indra/llui/lltabcontainer.cpp

https://bitbucket.org/lindenlab/viewer-beta/
C++ | 2048 lines | 1704 code | 220 blank | 124 comment | 321 complexity | b571075e307edeafc533c658902f4ba4 MD5 | raw file

Large files files are truncated, but you can click here to view the full file

   1/** 
   2 * @file lltabcontainer.cpp
   3 * @brief LLTabContainer class
   4 *
   5 * $LicenseInfo:firstyear=2001&license=viewerlgpl$
   6 * Second Life Viewer Source Code
   7 * Copyright (C) 2010, Linden Research, Inc.
   8 * 
   9 * This library is free software; you can redistribute it and/or
  10 * modify it under the terms of the GNU Lesser General Public
  11 * License as published by the Free Software Foundation;
  12 * version 2.1 of the License only.
  13 * 
  14 * This library is distributed in the hope that it will be useful,
  15 * but WITHOUT ANY WARRANTY; without even the implied warranty of
  16 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
  17 * Lesser General Public License for more details.
  18 * 
  19 * You should have received a copy of the GNU Lesser General Public
  20 * License along with this library; if not, write to the Free Software
  21 * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA  02110-1301  USA
  22 * 
  23 * Linden Research, Inc., 945 Battery Street, San Francisco, CA  94111  USA
  24 * $/LicenseInfo$
  25 */
  26
  27#include "linden_common.h"
  28
  29#include "lltabcontainer.h"
  30
  31#include "llfocusmgr.h"
  32#include "lllocalcliprect.h"
  33#include "llrect.h"
  34#include "llresizehandle.h"
  35#include "lltextbox.h"
  36#include "llcriticaldamp.h"
  37#include "lluictrlfactory.h"
  38#include "llrender.h"
  39#include "llfloater.h"
  40#include "lltrans.h"
  41
  42//----------------------------------------------------------------------------
  43
  44// Implementation Notes:
  45//  - Each tab points to a LLPanel (see LLTabTuple below)
  46//  - When a tab is selected, the validation callback
  47//    (LLUICtrl::mValidateSignal) is called
  48//  -  If the validation callback returns true (or none is provided),
  49//     the tab is changed and the commit callback
  50//     (LLUICtrl::mCommitSignal) is called
  51//  - Callbacks pass the LLTabContainer as the control,
  52//    and the NAME of the selected PANEL as the LLSD data
  53
  54//----------------------------------------------------------------------------
  55
  56const F32 SCROLL_STEP_TIME = 0.4f;
  57const F32 SCROLL_DELAY_TIME = 0.5f;
  58
  59void LLTabContainer::TabPositions::declareValues()
  60{
  61	declare("top", LLTabContainer::TOP);
  62	declare("bottom", LLTabContainer::BOTTOM);
  63	declare("left", LLTabContainer::LEFT);
  64}
  65
  66//----------------------------------------------------------------------------
  67
  68// Structure used to map tab buttons to and from tab panels
  69class LLTabTuple
  70{
  71public:
  72	LLTabTuple( LLTabContainer* c, LLPanel* p, LLButton* b, LLTextBox* placeholder = NULL)
  73		:
  74		mTabContainer(c),
  75		mTabPanel(p),
  76		mButton(b),
  77		mOldState(FALSE),
  78		mPlaceholderText(placeholder),
  79		mPadding(0)
  80	{}
  81
  82	LLTabContainer*  mTabContainer;
  83	LLPanel*		 mTabPanel;
  84	LLButton*		 mButton;
  85	BOOL			 mOldState;
  86	LLTextBox*		 mPlaceholderText;
  87	S32				 mPadding;
  88};
  89
  90//----------------------------------------------------------------------------
  91
  92//============================================================================
  93/*
  94 * @file lltabcontainer.cpp
  95 * @brief class implements LLButton with LLIconCtrl on it
  96 */
  97class LLCustomButtonIconCtrl : public LLButton
  98{
  99public:
 100	struct Params
 101	:	public LLInitParam::Block<Params, LLButton::Params>
 102	{
 103		// LEFT, RIGHT, TOP, BOTTOM paddings of LLIconCtrl in this class has same value
 104		Optional<S32>					icon_ctrl_pad;
 105
 106		Params()
 107		:	icon_ctrl_pad("icon_ctrl_pad", 1)
 108		{}
 109	};
 110
 111protected:
 112	friend class LLUICtrlFactory;
 113
 114	LLCustomButtonIconCtrl(const Params& p)
 115	:	LLButton(p),
 116		mIcon(NULL),
 117		mIconAlignment(LLFontGL::HCENTER),
 118		mIconCtrlPad(p.icon_ctrl_pad)
 119	{}
 120
 121public:
 122
 123	void updateLayout()
 124	{
 125		LLRect button_rect = getRect();
 126		LLRect icon_rect = mIcon->getRect();
 127
 128		S32 icon_size = button_rect.getHeight() - 2*mIconCtrlPad;
 129
 130		switch(mIconAlignment)
 131		{
 132		case LLFontGL::LEFT:
 133			icon_rect.setLeftTopAndSize(button_rect.mLeft + mIconCtrlPad, button_rect.mTop - mIconCtrlPad, 
 134				icon_size, icon_size);
 135			setLeftHPad(icon_size + mIconCtrlPad * 2);
 136			break;
 137		case LLFontGL::HCENTER:
 138			icon_rect.setLeftTopAndSize(button_rect.mRight - (button_rect.getWidth() + mIconCtrlPad - icon_size)/2, button_rect.mTop - mIconCtrlPad, 
 139				icon_size, icon_size);
 140			setRightHPad(icon_size + mIconCtrlPad * 2);
 141			break;
 142		case LLFontGL::RIGHT:
 143			icon_rect.setLeftTopAndSize(button_rect.mRight - mIconCtrlPad - icon_size, button_rect.mTop - mIconCtrlPad, 
 144				icon_size, icon_size);
 145			setRightHPad(icon_size + mIconCtrlPad * 2);
 146			break;
 147		default:
 148			break;
 149		}
 150		mIcon->setRect(icon_rect);
 151	}
 152
 153	void setIcon(LLIconCtrl* icon, LLFontGL::HAlign alignment = LLFontGL::LEFT)
 154	{
 155		if(icon)
 156		{
 157			if(mIcon)
 158			{
 159				removeChild(mIcon);
 160				mIcon->die();
 161			}
 162			mIcon = icon;
 163			mIconAlignment = alignment;
 164
 165			addChild(mIcon);
 166			updateLayout();
 167		}
 168	}
 169
 170	LLIconCtrl* getIconCtrl() const
 171	{
 172		return mIcon;
 173	}
 174
 175private:
 176	LLIconCtrl* mIcon;
 177	LLFontGL::HAlign mIconAlignment;
 178	S32 mIconCtrlPad;
 179};
 180//============================================================================
 181
 182struct LLPlaceHolderPanel : public LLPanel
 183{
 184	// create dummy param block to register with "placeholder" nane
 185	struct Params : public LLPanel::Params{};
 186	LLPlaceHolderPanel(const Params& p) : LLPanel(p)
 187	{}
 188};
 189static LLDefaultChildRegistry::Register<LLPlaceHolderPanel> r1("placeholder");
 190static LLDefaultChildRegistry::Register<LLTabContainer> r2("tab_container");
 191
 192LLTabContainer::TabParams::TabParams()
 193:	tab_top_image_unselected("tab_top_image_unselected"),
 194	tab_top_image_selected("tab_top_image_selected"),
 195	tab_top_image_flash("tab_top_image_flash"),
 196	tab_bottom_image_unselected("tab_bottom_image_unselected"),
 197	tab_bottom_image_selected("tab_bottom_image_selected"),
 198	tab_bottom_image_flash("tab_bottom_image_flash"),
 199	tab_left_image_unselected("tab_left_image_unselected"),
 200	tab_left_image_selected("tab_left_image_selected"),
 201	tab_left_image_flash("tab_left_image_flash")
 202{}
 203
 204LLTabContainer::Params::Params()
 205:	tab_width("tab_width"),
 206	tab_min_width("tab_min_width"),
 207	tab_max_width("tab_max_width"),
 208	tab_height("tab_height"),
 209	label_pad_bottom("label_pad_bottom"),
 210	label_pad_left("label_pad_left"),
 211	tab_position("tab_position"),
 212	hide_tabs("hide_tabs", false),
 213	tab_padding_right("tab_padding_right"),
 214	first_tab("first_tab"),
 215	middle_tab("middle_tab"),
 216	last_tab("last_tab"),
 217	use_custom_icon_ctrl("use_custom_icon_ctrl", false),
 218	open_tabs_on_drag_and_drop("open_tabs_on_drag_and_drop", false),
 219	tab_icon_ctrl_pad("tab_icon_ctrl_pad", 0),
 220	use_ellipses("use_ellipses"),
 221	font_halign("halign")
 222{}
 223
 224LLTabContainer::LLTabContainer(const LLTabContainer::Params& p)
 225:	LLPanel(p),
 226	mCurrentTabIdx(-1),
 227	mTabsHidden(p.hide_tabs),
 228	mScrolled(FALSE),
 229	mScrollPos(0),
 230	mScrollPosPixels(0),
 231	mMaxScrollPos(0),
 232	mTitleBox(NULL),
 233	mTopBorderHeight(LLPANEL_BORDER_WIDTH),
 234	mLockedTabCount(0),
 235	mMinTabWidth(0),
 236	mMaxTabWidth(p.tab_max_width),
 237	mTabHeight(p.tab_height),
 238	mLabelPadBottom(p.label_pad_bottom),
 239	mLabelPadLeft(p.label_pad_left),
 240	mPrevArrowBtn(NULL),
 241	mNextArrowBtn(NULL),
 242	mIsVertical( p.tab_position == LEFT ),
 243	// Horizontal Specific
 244	mJumpPrevArrowBtn(NULL),
 245	mJumpNextArrowBtn(NULL),
 246	mRightTabBtnOffset(p.tab_padding_right),
 247	mTotalTabWidth(0),
 248	mTabPosition(p.tab_position),
 249	mFontHalign(p.font_halign),
 250	mFont(p.font),
 251	mFirstTabParams(p.first_tab),
 252	mMiddleTabParams(p.middle_tab),
 253	mLastTabParams(p.last_tab),
 254	mCustomIconCtrlUsed(p.use_custom_icon_ctrl),
 255	mOpenTabsOnDragAndDrop(p.open_tabs_on_drag_and_drop),
 256	mTabIconCtrlPad(p.tab_icon_ctrl_pad),
 257	mUseTabEllipses(p.use_ellipses)
 258{
 259	static LLUICachedControl<S32> tabcntr_vert_tab_min_width ("UITabCntrVertTabMinWidth", 0);
 260
 261	mDragAndDropDelayTimer.stop();
 262
 263	if (p.tab_width.isProvided())
 264	{
 265		mMinTabWidth = p.tab_width;
 266	}
 267	else if (!mIsVertical)
 268	{
 269		mMinTabWidth = p.tab_min_width;
 270	}
 271	else
 272	{
 273		// *HACK: support default min width for legacy vertical
 274		// tab containers
 275		mMinTabWidth = tabcntr_vert_tab_min_width;
 276	}
 277
 278	initButtons( );
 279}
 280
 281LLTabContainer::~LLTabContainer()
 282{
 283	std::for_each(mTabList.begin(), mTabList.end(), DeletePointer());
 284}
 285
 286//virtual
 287void LLTabContainer::setValue(const LLSD& value)
 288{
 289	selectTab((S32) value.asInteger());
 290}
 291
 292//virtual
 293void LLTabContainer::reshape(S32 width, S32 height, BOOL called_from_parent)
 294{
 295	LLPanel::reshape( width, height, called_from_parent );
 296	updateMaxScrollPos();
 297}
 298
 299//virtual
 300LLView* LLTabContainer::getChildView(const std::string& name, BOOL recurse) const
 301{
 302	tuple_list_t::const_iterator itor;
 303	for (itor = mTabList.begin(); itor != mTabList.end(); ++itor)
 304	{
 305		LLPanel *panel = (*itor)->mTabPanel;
 306		if (panel->getName() == name)
 307		{
 308			return panel;
 309		}
 310	}
 311
 312	if (recurse)
 313	{
 314		for (itor = mTabList.begin(); itor != mTabList.end(); ++itor)
 315		{
 316			LLPanel *panel = (*itor)->mTabPanel;
 317			LLView *child = panel->getChildView(name, recurse);
 318			if (child)
 319			{
 320				return child;
 321			}
 322		}
 323	}
 324	return LLView::getChildView(name, recurse);
 325}
 326
 327//virtual
 328LLView* LLTabContainer::findChildView(const std::string& name, BOOL recurse) const
 329{
 330	tuple_list_t::const_iterator itor;
 331	for (itor = mTabList.begin(); itor != mTabList.end(); ++itor)
 332	{
 333		LLPanel *panel = (*itor)->mTabPanel;
 334		if (panel->getName() == name)
 335		{
 336			return panel;
 337		}
 338	}
 339
 340	if (recurse)
 341	{
 342		for (itor = mTabList.begin(); itor != mTabList.end(); ++itor)
 343		{
 344			LLPanel *panel = (*itor)->mTabPanel;
 345			LLView *child = panel->findChildView(name, recurse);
 346			if (child)
 347			{
 348				return child;
 349			}
 350		}
 351	}
 352	return LLView::findChildView(name, recurse);
 353}
 354
 355bool LLTabContainer::addChild(LLView* view, S32 tab_group)
 356{
 357	LLPanel* panelp = dynamic_cast<LLPanel*>(view);
 358
 359	if (panelp)
 360	{
 361		addTabPanel(TabPanelParams().panel(panelp).label(panelp->getLabel()).is_placeholder(dynamic_cast<LLPlaceHolderPanel*>(view) != NULL));
 362		return true;
 363	}
 364	else
 365	{
 366		return LLUICtrl::addChild(view, tab_group);
 367	}
 368}
 369
 370BOOL LLTabContainer::postBuild()
 371{
 372	selectFirstTab();
 373
 374	return TRUE;
 375}
 376
 377// virtual
 378void LLTabContainer::draw()
 379{
 380	static LLUICachedControl<S32> tabcntrv_pad ("UITabCntrvPad", 0);
 381	static LLUICachedControl<S32> tabcntrv_arrow_btn_size ("UITabCntrvArrowBtnSize", 0);
 382	static LLUICachedControl<S32> tabcntr_tab_h_pad ("UITabCntrTabHPad", 0);
 383	static LLUICachedControl<S32> tabcntr_arrow_btn_size ("UITabCntrArrowBtnSize", 0);
 384	static LLUICachedControl<S32> tabcntr_tab_partial_width ("UITabCntrTabPartialWidth", 0);
 385	S32 target_pixel_scroll = 0;
 386	S32 cur_scroll_pos = getScrollPos();
 387	if (cur_scroll_pos > 0)
 388	{
 389		S32 available_width_with_arrows = getRect().getWidth() - mRightTabBtnOffset - 2 * (LLPANEL_BORDER_WIDTH + tabcntr_arrow_btn_size  + tabcntr_arrow_btn_size + 1);
 390		if (!mIsVertical)
 391		{
 392			for(tuple_list_t::iterator iter = mTabList.begin(); iter != mTabList.end(); ++iter)
 393			{
 394				if (cur_scroll_pos == 0)
 395				{
 396					break;
 397				}
 398				target_pixel_scroll += (*iter)->mButton->getRect().getWidth();
 399				cur_scroll_pos--;
 400			}
 401
 402			// Show part of the tab to the left of what is fully visible
 403			target_pixel_scroll -= tabcntr_tab_partial_width;
 404			// clamp so that rightmost tab never leaves right side of screen
 405			target_pixel_scroll = llmin(mTotalTabWidth - available_width_with_arrows, target_pixel_scroll);
 406		}
 407	}
 408
 409	setScrollPosPixels((S32)lerp((F32)getScrollPosPixels(), (F32)target_pixel_scroll, LLCriticalDamp::getInterpolant(0.08f)));
 410
 411	BOOL has_scroll_arrows = !getTabsHidden() && ((mMaxScrollPos > 0) || (mScrollPosPixels > 0));
 412	if (!mIsVertical)
 413	{
 414		mJumpPrevArrowBtn->setVisible( has_scroll_arrows );
 415		mJumpNextArrowBtn->setVisible( has_scroll_arrows );
 416	}
 417	mPrevArrowBtn->setVisible( has_scroll_arrows );
 418	mNextArrowBtn->setVisible( has_scroll_arrows );
 419
 420	S32 left = 0, top = 0;
 421	if (mIsVertical)
 422	{
 423		top = getRect().getHeight() - getTopBorderHeight() - LLPANEL_BORDER_WIDTH - 1 - (has_scroll_arrows ? tabcntrv_arrow_btn_size : 0);
 424		top += getScrollPosPixels();
 425	}
 426	else
 427	{
 428		// Set the leftmost position of the tab buttons.
 429		left = LLPANEL_BORDER_WIDTH + (has_scroll_arrows ? (tabcntr_arrow_btn_size * 2) : tabcntr_tab_h_pad);
 430		left -= getScrollPosPixels();
 431	}
 432	
 433	// Hide all the buttons
 434	if (getTabsHidden())
 435	{
 436		for(tuple_list_t::iterator iter = mTabList.begin(); iter != mTabList.end(); ++iter)
 437		{
 438			LLTabTuple* tuple = *iter;
 439			tuple->mButton->setVisible( FALSE );
 440		}
 441	}
 442
 443	{
 444		LLRect clip_rect = getLocalRect();
 445		clip_rect.mLeft+=(LLPANEL_BORDER_WIDTH + 2);
 446		clip_rect.mRight-=(LLPANEL_BORDER_WIDTH + 2);
 447		LLLocalClipRect clip(clip_rect);
 448		LLPanel::draw();
 449	}
 450
 451	// if tabs are hidden, don't draw them and leave them in the invisible state
 452	if (!getTabsHidden())
 453	{
 454		// Show all the buttons
 455		for(tuple_list_t::iterator iter = mTabList.begin(); iter != mTabList.end(); ++iter)
 456		{
 457			LLTabTuple* tuple = *iter;
 458			tuple->mButton->setVisible( TRUE );
 459		}
 460
 461		S32 max_scroll_visible = getTabCount() - getMaxScrollPos() + getScrollPos();
 462		S32 idx = 0;
 463		for(tuple_list_t::iterator iter = mTabList.begin(); iter != mTabList.end(); ++iter)
 464		{
 465			LLTabTuple* tuple = *iter;
 466
 467			tuple->mButton->translate( left ? left - tuple->mButton->getRect().mLeft : 0,
 468									   top ? top - tuple->mButton->getRect().mTop : 0 );
 469			if (top) top -= BTN_HEIGHT + tabcntrv_pad;
 470			if (left) left += tuple->mButton->getRect().getWidth();
 471
 472			if (!mIsVertical)
 473			{
 474				if( idx < getScrollPos() )
 475				{
 476					if( tuple->mButton->getFlashing() )
 477					{
 478						mPrevArrowBtn->setFlashing( TRUE );
 479					}
 480				}
 481				else if( max_scroll_visible < idx )
 482				{
 483					if( tuple->mButton->getFlashing() )
 484					{
 485						mNextArrowBtn->setFlashing( TRUE );
 486					}
 487				}
 488			}
 489
 490			idx++;
 491		}
 492
 493
 494		if( mIsVertical && has_scroll_arrows )
 495		{
 496			// Redraw the arrows so that they appears on top.
 497			gGL.pushUIMatrix();
 498			gGL.translateUI((F32)mPrevArrowBtn->getRect().mLeft, (F32)mPrevArrowBtn->getRect().mBottom, 0.f);
 499			mPrevArrowBtn->draw();
 500			gGL.popUIMatrix();
 501
 502			gGL.pushUIMatrix();
 503			gGL.translateUI((F32)mNextArrowBtn->getRect().mLeft, (F32)mNextArrowBtn->getRect().mBottom, 0.f);
 504			mNextArrowBtn->draw();
 505			gGL.popUIMatrix();
 506		}
 507	}
 508
 509	mPrevArrowBtn->setFlashing(FALSE);
 510	mNextArrowBtn->setFlashing(FALSE);
 511}
 512
 513
 514// virtual
 515BOOL LLTabContainer::handleMouseDown( S32 x, S32 y, MASK mask )
 516{
 517	static LLUICachedControl<S32> tabcntrv_pad ("UITabCntrvPad", 0);
 518	BOOL handled = FALSE;
 519	BOOL has_scroll_arrows = (getMaxScrollPos() > 0) && !getTabsHidden();
 520
 521	if (has_scroll_arrows)
 522	{
 523		if (mJumpPrevArrowBtn&& mJumpPrevArrowBtn->getRect().pointInRect(x, y))
 524		{
 525			S32 local_x = x - mJumpPrevArrowBtn->getRect().mLeft;
 526			S32 local_y = y - mJumpPrevArrowBtn->getRect().mBottom;
 527			handled = mJumpPrevArrowBtn->handleMouseDown(local_x, local_y, mask);
 528		}
 529		else if (mJumpNextArrowBtn && mJumpNextArrowBtn->getRect().pointInRect(x, y))
 530		{
 531			S32 local_x = x - mJumpNextArrowBtn->getRect().mLeft;
 532			S32 local_y = y - mJumpNextArrowBtn->getRect().mBottom;
 533			handled = mJumpNextArrowBtn->handleMouseDown(local_x, local_y, mask);
 534		}
 535		else if (mPrevArrowBtn && mPrevArrowBtn->getRect().pointInRect(x, y))
 536		{
 537			S32 local_x = x - mPrevArrowBtn->getRect().mLeft;
 538			S32 local_y = y - mPrevArrowBtn->getRect().mBottom;
 539			handled = mPrevArrowBtn->handleMouseDown(local_x, local_y, mask);
 540		}
 541		else if (mNextArrowBtn && mNextArrowBtn->getRect().pointInRect(x, y))
 542		{
 543			S32 local_x = x - mNextArrowBtn->getRect().mLeft;
 544			S32 local_y = y - mNextArrowBtn->getRect().mBottom;
 545			handled = mNextArrowBtn->handleMouseDown(local_x, local_y, mask);
 546		}
 547	}
 548	if (!handled)
 549	{
 550		handled = LLPanel::handleMouseDown( x, y, mask );
 551	}
 552
 553	S32 tab_count = getTabCount();
 554	if (tab_count > 0 && !getTabsHidden())
 555	{
 556		LLTabTuple* firsttuple = getTab(0);
 557		LLRect tab_rect;
 558		if (mIsVertical)
 559		{
 560			tab_rect = LLRect(firsttuple->mButton->getRect().mLeft,
 561								has_scroll_arrows ? mPrevArrowBtn->getRect().mBottom - tabcntrv_pad : mPrevArrowBtn->getRect().mTop,
 562								firsttuple->mButton->getRect().mRight,
 563								has_scroll_arrows ? mNextArrowBtn->getRect().mTop + tabcntrv_pad : mNextArrowBtn->getRect().mBottom );
 564		}
 565		else
 566		{
 567			tab_rect = LLRect(has_scroll_arrows ? mPrevArrowBtn->getRect().mRight : mJumpPrevArrowBtn->getRect().mLeft,
 568								firsttuple->mButton->getRect().mTop,
 569								has_scroll_arrows ? mNextArrowBtn->getRect().mLeft : mJumpNextArrowBtn->getRect().mRight,
 570								firsttuple->mButton->getRect().mBottom );
 571		}
 572		if( tab_rect.pointInRect( x, y ) )
 573		{
 574			S32 index = getCurrentPanelIndex();
 575			index = llclamp(index, 0, tab_count-1);
 576			LLButton* tab_button = getTab(index)->mButton;
 577			gFocusMgr.setMouseCapture(this);
 578			tab_button->setFocus(TRUE);
 579		}
 580	}
 581	return handled;
 582}
 583
 584// virtual
 585BOOL LLTabContainer::handleHover( S32 x, S32 y, MASK mask )
 586{
 587	BOOL handled = FALSE;
 588	BOOL has_scroll_arrows = (getMaxScrollPos() > 0) && !getTabsHidden();
 589
 590	if (has_scroll_arrows)
 591	{
 592		if (mJumpPrevArrowBtn && mJumpPrevArrowBtn->getRect().pointInRect(x, y))
 593		{
 594			S32 local_x = x - mJumpPrevArrowBtn->getRect().mLeft;
 595			S32 local_y = y - mJumpPrevArrowBtn->getRect().mBottom;
 596			handled = mJumpPrevArrowBtn->handleHover(local_x, local_y, mask);
 597		}
 598		else if (mJumpNextArrowBtn && mJumpNextArrowBtn->getRect().pointInRect(x, y))
 599		{
 600			S32 local_x = x - mJumpNextArrowBtn->getRect().mLeft;
 601			S32 local_y = y - mJumpNextArrowBtn->getRect().mBottom;
 602			handled = mJumpNextArrowBtn->handleHover(local_x, local_y, mask);
 603		}
 604		else if (mPrevArrowBtn && mPrevArrowBtn->getRect().pointInRect(x, y))
 605		{
 606			S32 local_x = x - mPrevArrowBtn->getRect().mLeft;
 607			S32 local_y = y - mPrevArrowBtn->getRect().mBottom;
 608			handled = mPrevArrowBtn->handleHover(local_x, local_y, mask);
 609		}
 610		else if (mNextArrowBtn && mNextArrowBtn->getRect().pointInRect(x, y))
 611		{
 612			S32 local_x = x - mNextArrowBtn->getRect().mLeft;
 613			S32 local_y = y - mNextArrowBtn->getRect().mBottom;
 614			handled = mNextArrowBtn->handleHover(local_x, local_y, mask);
 615		}
 616	}
 617	if (!handled)
 618	{
 619		handled = LLPanel::handleHover(x, y, mask);
 620	}
 621
 622	commitHoveredButton(x, y);
 623	return handled;
 624}
 625
 626// virtual
 627BOOL LLTabContainer::handleMouseUp( S32 x, S32 y, MASK mask )
 628{
 629	BOOL handled = FALSE;
 630	BOOL has_scroll_arrows = (getMaxScrollPos() > 0)  && !getTabsHidden();
 631
 632	if (has_scroll_arrows)
 633	{
 634		if (mJumpPrevArrowBtn && mJumpPrevArrowBtn->getRect().pointInRect(x, y))
 635		{
 636			S32 local_x = x - mJumpPrevArrowBtn->getRect().mLeft;
 637			S32 local_y = y - mJumpPrevArrowBtn->getRect().mBottom;
 638			handled = mJumpPrevArrowBtn->handleMouseUp(local_x, local_y, mask);
 639		}
 640		else if (mJumpNextArrowBtn && mJumpNextArrowBtn->getRect().pointInRect(x,	y))
 641		{
 642			S32	local_x	= x	- mJumpNextArrowBtn->getRect().mLeft;
 643			S32	local_y	= y	- mJumpNextArrowBtn->getRect().mBottom;
 644			handled = mJumpNextArrowBtn->handleMouseUp(local_x,	local_y, mask);
 645		}
 646		else if (mPrevArrowBtn && mPrevArrowBtn->getRect().pointInRect(x, y))
 647		{
 648			S32 local_x = x - mPrevArrowBtn->getRect().mLeft;
 649			S32 local_y = y - mPrevArrowBtn->getRect().mBottom;
 650			handled = mPrevArrowBtn->handleMouseUp(local_x, local_y, mask);
 651		}
 652		else if (mNextArrowBtn && mNextArrowBtn->getRect().pointInRect(x, y))
 653		{
 654			S32 local_x = x - mNextArrowBtn->getRect().mLeft;
 655			S32 local_y = y - mNextArrowBtn->getRect().mBottom;
 656			handled = mNextArrowBtn->handleMouseUp(local_x, local_y, mask);
 657		}
 658	}
 659	if (!handled)
 660	{
 661		handled = LLPanel::handleMouseUp( x, y, mask );
 662	}
 663
 664	commitHoveredButton(x, y);
 665	LLPanel* cur_panel = getCurrentPanel();
 666	if (hasMouseCapture())
 667	{
 668		if (cur_panel)
 669		{
 670			if (!cur_panel->focusFirstItem(FALSE))
 671			{
 672				// if nothing in the panel gets focus, make sure the new tab does
 673				// otherwise the last tab might keep focus
 674				getTab(getCurrentPanelIndex())->mButton->setFocus(TRUE);
 675			}
 676		}
 677		gFocusMgr.setMouseCapture(NULL);
 678	}
 679	return handled;
 680}
 681
 682// virtual
 683BOOL LLTabContainer::handleToolTip( S32 x, S32 y, MASK mask)
 684{
 685	static LLUICachedControl<S32> tabcntrv_pad ("UITabCntrvPad", 0);
 686	BOOL handled = LLPanel::handleToolTip( x, y, mask);
 687	if (!handled && getTabCount() > 0 && !getTabsHidden()) 
 688	{
 689		LLTabTuple* firsttuple = getTab(0);
 690
 691		BOOL has_scroll_arrows = (getMaxScrollPos() > 0);
 692		LLRect clip;
 693		if (mIsVertical)
 694		{
 695			clip = LLRect(firsttuple->mButton->getRect().mLeft,
 696						  has_scroll_arrows ? mPrevArrowBtn->getRect().mBottom - tabcntrv_pad : mPrevArrowBtn->getRect().mTop,
 697						  firsttuple->mButton->getRect().mRight,
 698						  has_scroll_arrows ? mNextArrowBtn->getRect().mTop + tabcntrv_pad : mNextArrowBtn->getRect().mBottom );
 699		}
 700		else
 701		{
 702			clip = LLRect(has_scroll_arrows ? mPrevArrowBtn->getRect().mRight : mJumpPrevArrowBtn->getRect().mLeft,
 703						  firsttuple->mButton->getRect().mTop,
 704						  has_scroll_arrows ? mNextArrowBtn->getRect().mLeft : mJumpNextArrowBtn->getRect().mRight,
 705						  firsttuple->mButton->getRect().mBottom );
 706		}
 707
 708		if( clip.pointInRect( x, y ) )
 709		{
 710			for(tuple_list_t::iterator iter = mTabList.begin(); iter != mTabList.end(); ++iter)
 711			{
 712				LLTabTuple* tuple = *iter;
 713				tuple->mButton->setVisible( TRUE );
 714				S32 local_x = x - tuple->mButton->getRect().mLeft;
 715				S32 local_y = y - tuple->mButton->getRect().mBottom;
 716				handled = tuple->mButton->handleToolTip( local_x, local_y, mask);
 717				if( handled )
 718				{
 719					break;
 720				}
 721			}
 722		}
 723	}
 724	return handled;
 725}
 726
 727// virtual
 728BOOL LLTabContainer::handleKeyHere(KEY key, MASK mask)
 729{
 730	BOOL handled = FALSE;
 731	if (key == KEY_LEFT && mask == MASK_ALT)
 732	{
 733		selectPrevTab();
 734		handled = TRUE;
 735	}
 736	else if (key == KEY_RIGHT && mask == MASK_ALT)
 737	{
 738		selectNextTab();
 739		handled = TRUE;
 740	}
 741
 742	if (handled)
 743	{
 744		if (getCurrentPanel())
 745		{
 746			getCurrentPanel()->setFocus(TRUE);
 747		}
 748	}
 749
 750	if (!gFocusMgr.childHasKeyboardFocus(getCurrentPanel()))
 751	{
 752		// if child has focus, but not the current panel, focus is on a button
 753		if (mIsVertical)
 754		{
 755			switch(key)
 756			{
 757			  case KEY_UP:
 758				selectPrevTab();
 759				handled = TRUE;
 760				break;
 761			  case KEY_DOWN:
 762				selectNextTab();
 763				handled = TRUE;
 764				break;
 765			  case KEY_LEFT:
 766				handled = TRUE;
 767				break;
 768			  case KEY_RIGHT:
 769				if (getTabPosition() == LEFT && getCurrentPanel())
 770				{
 771					getCurrentPanel()->setFocus(TRUE);
 772				}
 773				handled = TRUE;
 774				break;
 775			  default:
 776				break;
 777			}
 778		}
 779		else
 780		{
 781			switch(key)
 782			{
 783			  case KEY_UP:
 784				if (getTabPosition() == BOTTOM && getCurrentPanel())
 785				{
 786					getCurrentPanel()->setFocus(TRUE);
 787				}
 788				handled = TRUE;
 789				break;
 790			  case KEY_DOWN:
 791				if (getTabPosition() == TOP && getCurrentPanel())
 792				{
 793					getCurrentPanel()->setFocus(TRUE);
 794				}
 795				handled = TRUE;
 796				break;
 797			  case KEY_LEFT:
 798				selectPrevTab();
 799				handled = TRUE;
 800				break;
 801			  case KEY_RIGHT:
 802				selectNextTab();
 803				handled = TRUE;
 804				break;
 805			  default:
 806				break;
 807			}
 808		}
 809	}
 810	return handled;
 811}
 812
 813// virtual
 814BOOL LLTabContainer::handleDragAndDrop(S32 x, S32 y, MASK mask,	BOOL drop,	EDragAndDropType type, void* cargo_data, EAcceptance *accept, std::string	&tooltip)
 815{
 816	BOOL has_scroll_arrows = (getMaxScrollPos() > 0);
 817
 818	if(mOpenTabsOnDragAndDrop && !getTabsHidden())
 819	{
 820		// In that case, we'll open the hovered tab while dragging and dropping items.
 821		// This allows for drilling through tabs.
 822		if (mDragAndDropDelayTimer.getStarted())
 823		{
 824			if (mDragAndDropDelayTimer.getElapsedTimeF32() > SCROLL_DELAY_TIME)
 825			{
 826				if (has_scroll_arrows)
 827				{
 828					if (mJumpPrevArrowBtn && mJumpPrevArrowBtn->getRect().pointInRect(x, y))
 829					{
 830						S32	local_x	= x	- mJumpPrevArrowBtn->getRect().mLeft;
 831						S32	local_y	= y	- mJumpPrevArrowBtn->getRect().mBottom;
 832						mJumpPrevArrowBtn->handleHover(local_x,	local_y, mask);
 833					}
 834					if (mJumpNextArrowBtn && mJumpNextArrowBtn->getRect().pointInRect(x, y))
 835					{
 836						S32	local_x	= x	- mJumpNextArrowBtn->getRect().mLeft;
 837						S32	local_y	= y	- mJumpNextArrowBtn->getRect().mBottom;
 838						mJumpNextArrowBtn->handleHover(local_x,	local_y, mask);
 839					}
 840					if (mPrevArrowBtn->getRect().pointInRect(x,	y))
 841					{
 842						S32	local_x	= x	- mPrevArrowBtn->getRect().mLeft;
 843						S32	local_y	= y	- mPrevArrowBtn->getRect().mBottom;
 844						mPrevArrowBtn->handleHover(local_x,	local_y, mask);
 845					}
 846					else if	(mNextArrowBtn->getRect().pointInRect(x, y))
 847					{
 848						S32	local_x	= x	- mNextArrowBtn->getRect().mLeft;
 849						S32	local_y	= y	- mNextArrowBtn->getRect().mBottom;
 850						mNextArrowBtn->handleHover(local_x, local_y, mask);
 851					}
 852				}
 853
 854				for(tuple_list_t::iterator iter	= mTabList.begin();	iter !=	 mTabList.end(); ++iter)
 855				{
 856					LLTabTuple*	tuple =	*iter;
 857					tuple->mButton->setVisible(	TRUE );
 858					S32	local_x	= x	- tuple->mButton->getRect().mLeft;
 859					S32	local_y	= y	- tuple->mButton->getRect().mBottom;
 860					if (tuple->mButton->pointInView(local_x, local_y) &&  tuple->mButton->getEnabled() && !tuple->mTabPanel->getVisible())
 861					{
 862						tuple->mButton->onCommit();
 863					}
 864				}
 865				// Stop the timer whether successful or not. Don't let it run forever.
 866				mDragAndDropDelayTimer.stop();
 867			}
 868		}
 869		else 
 870		{
 871			// Start a timer so we don't open tabs as soon as we hover on them
 872			mDragAndDropDelayTimer.start();
 873		}
 874	}
 875
 876	return LLView::handleDragAndDrop(x,	y, mask, drop, type, cargo_data,  accept, tooltip);
 877}
 878
 879void LLTabContainer::addTabPanel(LLPanel* panelp)
 880{
 881	addTabPanel(TabPanelParams().panel(panelp));
 882}
 883
 884// function to update images
 885void LLTabContainer::update_images(LLTabTuple* tuple, TabParams params, LLTabContainer::TabPosition pos)
 886{
 887	if (tuple && tuple->mButton)
 888	{
 889		if (pos == LLTabContainer::TOP)
 890		{
 891			tuple->mButton->setImageUnselected(static_cast<LLUIImage*>(params.tab_top_image_unselected));
 892			tuple->mButton->setImageSelected(static_cast<LLUIImage*>(params.tab_top_image_selected));
 893			tuple->mButton->setImageFlash(static_cast<LLUIImage*>(params.tab_top_image_flash));
 894		}
 895		else if (pos == LLTabContainer::BOTTOM)
 896		{
 897			tuple->mButton->setImageUnselected(static_cast<LLUIImage*>(params.tab_bottom_image_unselected));
 898			tuple->mButton->setImageSelected(static_cast<LLUIImage*>(params.tab_bottom_image_selected));
 899			tuple->mButton->setImageFlash(static_cast<LLUIImage*>(params.tab_bottom_image_flash));
 900		}
 901		else if (pos == LLTabContainer::LEFT)
 902		{
 903			tuple->mButton->setImageUnselected(static_cast<LLUIImage*>(params.tab_left_image_unselected));
 904			tuple->mButton->setImageSelected(static_cast<LLUIImage*>(params.tab_left_image_selected));
 905			tuple->mButton->setImageFlash(static_cast<LLUIImage*>(params.tab_left_image_flash));
 906		}
 907	}
 908}
 909
 910void LLTabContainer::addTabPanel(const TabPanelParams& panel)
 911{
 912	LLPanel* child = panel.panel();
 913
 914	llassert(child);
 915	if (!child) return;
 916
 917	const std::string& label = panel.label.isProvided() 
 918			? panel.label() 
 919			: panel.panel()->getLabel();
 920	BOOL select = panel.select_tab(); 
 921	S32 indent = panel.indent();
 922	BOOL placeholder = panel.is_placeholder;
 923	eInsertionPoint insertion_point = panel.insert_at();
 924
 925	static LLUICachedControl<S32> tabcntrv_pad ("UITabCntrvPad", 0);
 926	static LLUICachedControl<S32> tabcntr_button_panel_overlap ("UITabCntrButtonPanelOverlap", 0);
 927	static LLUICachedControl<S32> tab_padding ("UITabPadding", 0);
 928	if (child->getParent() == this)
 929	{
 930		// already a child of mine
 931		return;
 932	}
 933
 934	// Store the original label for possible xml export.
 935	child->setLabel(label);
 936	std::string trimmed_label = label;
 937	LLStringUtil::trim(trimmed_label);
 938
 939	S32 button_width = mMinTabWidth;
 940	if (!mIsVertical)
 941	{
 942		button_width = llclamp(mFont->getWidth(trimmed_label) + tab_padding, mMinTabWidth, mMaxTabWidth);
 943	}
 944	
 945	// Tab panel
 946	S32 tab_panel_top;
 947	S32 tab_panel_bottom;
 948	if (!getTabsHidden()) 
 949	{
 950		if( getTabPosition() == LLTabContainer::TOP )
 951		{
 952			S32 tab_height = mIsVertical ? BTN_HEIGHT : mTabHeight;
 953			tab_panel_top = getRect().getHeight() - getTopBorderHeight() - (tab_height - tabcntr_button_panel_overlap);	
 954			tab_panel_bottom = LLPANEL_BORDER_WIDTH;
 955		}
 956		else
 957		{
 958			tab_panel_top = getRect().getHeight() - getTopBorderHeight();
 959			tab_panel_bottom = (mTabHeight - tabcntr_button_panel_overlap);  // Run to the edge, covering up the border
 960		}
 961	}
 962	else
 963	{
 964		//Scip tab button space if they are invisible(EXT - 576)
 965		tab_panel_top = getRect().getHeight();
 966		tab_panel_bottom = LLPANEL_BORDER_WIDTH;
 967	}
 968
 969	LLRect tab_panel_rect;
 970	if (!getTabsHidden() && mIsVertical)
 971	{
 972		tab_panel_rect = LLRect(mMinTabWidth + mRightTabBtnOffset + (LLPANEL_BORDER_WIDTH * 2) + tabcntrv_pad,
 973								getRect().getHeight() - LLPANEL_BORDER_WIDTH,
 974								getRect().getWidth() - LLPANEL_BORDER_WIDTH,
 975								LLPANEL_BORDER_WIDTH);
 976	}
 977	else
 978	{
 979		tab_panel_rect = LLRect(LLPANEL_BORDER_WIDTH, 
 980								tab_panel_top,
 981								getRect().getWidth()-LLPANEL_BORDER_WIDTH,
 982								tab_panel_bottom );
 983	}
 984	child->setFollowsAll();
 985	child->translate( tab_panel_rect.mLeft - child->getRect().mLeft, tab_panel_rect.mBottom - child->getRect().mBottom);
 986	child->reshape( tab_panel_rect.getWidth(), tab_panel_rect.getHeight(), TRUE );
 987	// add this child later
 988
 989	child->setVisible( FALSE );  // Will be made visible when selected
 990
 991	mTotalTabWidth += button_width;
 992
 993	// Tab button
 994	LLRect btn_rect;  // Note: btn_rect.mLeft is just a dummy.  Will be updated in draw().
 995	LLUIImage* tab_img = NULL;
 996	LLUIImage* tab_selected_img = NULL;
 997	S32 tab_fudge = 1;		//  To make new tab art look better, nudge buttons up 1 pel
 998
 999	if (mIsVertical)
1000	{
1001		btn_rect.setLeftTopAndSize(tabcntrv_pad + LLPANEL_BORDER_WIDTH + 2,	// JC - Fudge factor
1002								   (getRect().getHeight() - getTopBorderHeight() - LLPANEL_BORDER_WIDTH - 1) - ((BTN_HEIGHT + tabcntrv_pad) * getTabCount()),
1003								   mMinTabWidth,
1004								   BTN_HEIGHT);
1005	}
1006	else if( getTabPosition() == LLTabContainer::TOP )
1007	{
1008		btn_rect.setLeftTopAndSize( 0, getRect().getHeight() - getTopBorderHeight() + tab_fudge, button_width, mTabHeight);
1009		tab_img = mMiddleTabParams.tab_top_image_unselected;
1010		tab_selected_img = mMiddleTabParams.tab_top_image_selected; 
1011	}
1012	else
1013	{
1014		btn_rect.setOriginAndSize( 0, 0 + tab_fudge, button_width, mTabHeight);
1015		tab_img = mMiddleTabParams.tab_bottom_image_unselected;
1016		tab_selected_img = mMiddleTabParams.tab_bottom_image_selected;
1017	}
1018
1019	LLTextBox* textbox = NULL;
1020	LLButton* btn = NULL;
1021	LLCustomButtonIconCtrl::Params custom_btn_params;
1022	{
1023		custom_btn_params.icon_ctrl_pad(mTabIconCtrlPad);
1024	}
1025	LLButton::Params normal_btn_params;
1026	
1027	if (placeholder)
1028	{
1029		btn_rect.translate(0, -6); // *TODO: make configurable
1030		LLTextBox::Params params;
1031		params.name(trimmed_label);
1032		params.rect(btn_rect);
1033		params.initial_value(trimmed_label);
1034		params.font(mFont);
1035		textbox = LLUICtrlFactory::create<LLTextBox> (params);
1036		
1037		LLButton::Params p;
1038		p.name("placeholder");
1039		btn = LLUICtrlFactory::create<LLButton>(p);
1040	}
1041	else
1042	{
1043		LLButton::Params& p = (mCustomIconCtrlUsed ? custom_btn_params : normal_btn_params);
1044		
1045		p.rect(btn_rect);
1046		p.font(mFont);
1047		p.font_halign = mFontHalign;
1048		p.label(trimmed_label);
1049		p.click_callback.function(boost::bind(&LLTabContainer::onTabBtn, this, _2, child));
1050		if (indent)
1051		{
1052			p.pad_left(indent);
1053		}
1054		p.pad_bottom( mLabelPadBottom );
1055		p.scale_image(true);
1056		p.tab_stop(false);
1057		p.label_shadow(false);
1058		p.follows.flags = FOLLOWS_LEFT;
1059		
1060		if (mIsVertical)
1061		{
1062			p.name(std::string("vert tab button"));
1063			p.image_unselected(mMiddleTabParams.tab_left_image_unselected);
1064			p.image_selected(mMiddleTabParams.tab_left_image_selected);
1065			p.follows.flags = p.follows.flags() | FOLLOWS_TOP;
1066		}
1067		else
1068		{
1069			p.name(std::string(child->getName()) + " tab");
1070			p.visible(false);
1071			p.image_unselected(tab_img);
1072			p.image_selected(tab_selected_img);
1073			p.follows.flags = p.follows.flags() | (getTabPosition() == TOP ? FOLLOWS_TOP : FOLLOWS_BOTTOM);
1074			// Try to squeeze in a bit more text
1075			p.pad_left( mLabelPadLeft );
1076			p.pad_right(2);
1077		}
1078		
1079		// *TODO : It seems wrong not to use p in both cases considering the way p is initialized
1080		if (mCustomIconCtrlUsed)
1081		{
1082			btn = LLUICtrlFactory::create<LLCustomButtonIconCtrl>(custom_btn_params);
1083		}
1084		else
1085		{
1086			btn = LLUICtrlFactory::create<LLButton>(p);
1087		}
1088	}
1089	
1090	LLTabTuple* tuple = new LLTabTuple( this, child, btn, textbox );
1091	insertTuple( tuple, insertion_point );
1092
1093	// if new tab was added as a first or last tab, update button image 
1094	// and update button image of any tab it may have affected
1095	if (tuple == mTabList.front())
1096	{  
1097		update_images(tuple, mFirstTabParams, getTabPosition());
1098
1099		if (mTabList.size() == 2) 
1100		{		
1101			update_images(mTabList[1], mLastTabParams, getTabPosition());
1102		}
1103		else if (mTabList.size() > 2) 
1104		{
1105			update_images(mTabList[1], mMiddleTabParams, getTabPosition());
1106		}
1107	}
1108	else if (tuple == mTabList.back())
1109	{
1110		update_images(tuple, mLastTabParams, getTabPosition());
1111
1112		if (mTabList.size() > 2)
1113		{
1114			update_images(mTabList[mTabList.size()-2], mMiddleTabParams, getTabPosition());
1115		}
1116	}
1117
1118	//Don't add button and textbox if tab buttons are invisible(EXT - 576)
1119	if (!getTabsHidden())
1120	{
1121		if (textbox)
1122		{
1123			addChild( textbox, 0 );
1124		}
1125		if (btn)
1126		{
1127			addChild( btn, 0 );
1128		}
1129	}
1130
1131	if (child)
1132	{
1133		LLUICtrl::addChild(child, 1);
1134	}
1135
1136	sendChildToFront(mPrevArrowBtn);
1137	sendChildToFront(mNextArrowBtn);
1138	sendChildToFront(mJumpPrevArrowBtn);
1139	sendChildToFront(mJumpNextArrowBtn);
1140	
1141	if( select )
1142	{
1143		selectLastTab();
1144	}
1145
1146	updateMaxScrollPos();
1147}
1148
1149void LLTabContainer::addPlaceholder(LLPanel* child, const std::string& label)
1150{
1151	addTabPanel(TabPanelParams().panel(child).label(label).is_placeholder(true));
1152}
1153
1154void LLTabContainer::removeTabPanel(LLPanel* child)
1155{
1156	static LLUICachedControl<S32> tabcntrv_pad ("UITabCntrvPad", 0);
1157	if (mIsVertical)
1158	{
1159		// Fix-up button sizes
1160		S32 tab_count = 0;
1161		for(tuple_list_t::iterator iter = mTabList.begin(); iter != mTabList.end(); ++iter)
1162		{
1163			LLTabTuple* tuple = *iter;
1164			LLRect rect;
1165			rect.setLeftTopAndSize(tabcntrv_pad + LLPANEL_BORDER_WIDTH + 2,	// JC - Fudge factor
1166								   (getRect().getHeight() - LLPANEL_BORDER_WIDTH - 1) - ((BTN_HEIGHT + tabcntrv_pad) * (tab_count)),
1167								   mMinTabWidth,
1168								   BTN_HEIGHT);
1169			if (tuple->mPlaceholderText)
1170			{
1171				tuple->mPlaceholderText->setRect(rect);
1172			}
1173			else
1174			{
1175				tuple->mButton->setRect(rect);
1176			}
1177			tab_count++;
1178		}
1179	}
1180	else
1181	{
1182		// Adjust the total tab width.
1183		for(tuple_list_t::iterator iter = mTabList.begin(); iter != mTabList.end(); ++iter)
1184		{
1185			LLTabTuple* tuple = *iter;
1186			if( tuple->mTabPanel == child )
1187			{
1188				mTotalTabWidth -= tuple->mButton->getRect().getWidth();
1189				break;
1190			}
1191		}
1192	}
1193	
1194	BOOL has_focus = gFocusMgr.childHasKeyboardFocus(this);
1195
1196	// If the tab being deleted is the selected one, select a different tab.
1197	for(std::vector<LLTabTuple*>::iterator iter = mTabList.begin(); iter != mTabList.end(); ++iter)
1198	{
1199		LLTabTuple* tuple = *iter;
1200		if( tuple->mTabPanel == child )
1201		{
1202			// update tab button images if removing the first or last tab
1203			if ((tuple == mTabList.front()) && (mTabList.size() > 1))
1204			{
1205				update_images(mTabList[1], mFirstTabParams, getTabPosition());
1206			}
1207			else if ((tuple == mTabList.back()) && (mTabList.size() > 2))
1208			{
1209				update_images(mTabList[mTabList.size()-2], mLastTabParams, getTabPosition());
1210			}
1211
1212			removeChild( tuple->mButton );
1213 			delete tuple->mButton;
1214
1215 			removeChild( tuple->mTabPanel );
1216// 			delete tuple->mTabPanel;
1217			
1218			mTabList.erase( iter );
1219			delete tuple;
1220
1221			break;
1222		}
1223	}
1224
1225	// make sure we don't have more locked tabs than we have tabs
1226	mLockedTabCount = llmin(getTabCount(), mLockedTabCount);
1227
1228	if (mCurrentTabIdx >= (S32)mTabList.size())
1229	{
1230		mCurrentTabIdx = mTabList.size()-1;
1231	}
1232	selectTab(mCurrentTabIdx);
1233	if (has_focus)
1234	{
1235		LLPanel* panelp = getPanelByIndex(mCurrentTabIdx);
1236		if (panelp)
1237		{
1238			panelp->setFocus(TRUE);
1239		}
1240	}
1241
1242	updateMaxScrollPos();
1243}
1244
1245void LLTabContainer::lockTabs(S32 num_tabs)
1246{
1247	// count current tabs or use supplied value and ensure no new tabs get
1248	// inserted between them
1249	mLockedTabCount = num_tabs > 0 ? llmin(getTabCount(), num_tabs) : getTabCount();
1250}
1251
1252void LLTabContainer::unlockTabs()
1253{
1254	mLockedTabCount = 0;
1255}
1256
1257void LLTabContainer::enableTabButton(S32 which, BOOL enable)
1258{
1259	if (which >= 0 && which < (S32)mTabList.size())
1260	{
1261		mTabList[which]->mButton->setEnabled(enable);
1262	}
1263	// Stop the DaD timer as it might run forever
1264	// enableTabButton() is typically called on refresh and draw when anything changed
1265	// in the tab container so it's a good time to reset that.
1266	mDragAndDropDelayTimer.stop();
1267}
1268
1269void LLTabContainer::deleteAllTabs()
1270{
1271	// Remove all the tab buttons and delete them.  Also, unlink all the child panels.
1272	for(std::vector<LLTabTuple*>::iterator iter = mTabList.begin(); iter != mTabList.end(); ++iter)
1273	{
1274		LLTabTuple* tuple = *iter;
1275
1276		removeChild( tuple->mButton );
1277		delete tuple->mButton;
1278
1279 		removeChild( tuple->mTabPanel );
1280// 		delete tuple->mTabPanel;
1281	}
1282
1283	// Actually delete the tuples themselves
1284	std::for_each(mTabList.begin(), mTabList.end(), DeletePointer());
1285	mTabList.clear();
1286	
1287	// And there isn't a current tab any more
1288	mCurrentTabIdx = -1;
1289}
1290
1291LLPanel* LLTabContainer::getCurrentPanel()
1292{
1293	if (mCurrentTabIdx >= 0 && mCurrentTabIdx < (S32) mTabList.size())
1294	{
1295		return mTabList[mCurrentTabIdx]->mTabPanel;
1296	}
1297	return NULL;
1298}
1299
1300S32 LLTabContainer::getCurrentPanelIndex()
1301{
1302	return mCurrentTabIdx;
1303}
1304
1305S32 LLTabContainer::getTabCount()
1306{
1307	return mTabList.size();
1308}
1309
1310LLPanel* LLTabContainer::getPanelByIndex(S32 index)
1311{
1312	if (index >= 0 && index < (S32)mTabList.size())
1313	{
1314		return mTabList[index]->mTabPanel;
1315	}
1316	return NULL;
1317}
1318
1319S32 LLTabContainer::getIndexForPanel(LLPanel* panel)
1320{
1321	for (S32 index = 0; index < (S32)mTabList.size(); index++)
1322	{
1323		if (mTabList[index]->mTabPanel == panel)
1324		{
1325			return index;
1326		}
1327	}
1328	return -1;
1329}
1330
1331S32 LLTabContainer::getPanelIndexByTitle(const std::string& title)
1332{
1333	for (S32 index = 0 ; index < (S32)mTabList.size(); index++)
1334	{
1335		if (title == mTabList[index]->mButton->getLabelSelected())
1336		{
1337			return index;
1338		}
1339	}
1340	return -1;
1341}
1342
1343LLPanel* LLTabContainer::getPanelByName(const std::string& name)
1344{
1345	for (S32 index = 0 ; index < (S32)mTabList.size(); index++)
1346	{
1347		LLPanel *panel = mTabList[index]->mTabPanel;
1348		if (name == panel->getName())
1349		{
1350			return panel;
1351		}
1352	}
1353	return NULL;
1354}
1355
1356// Change the name of the button for the current tab.
1357void LLTabContainer::setCurrentTabName(const std::string& name)
1358{
1359	// Might not have a tab selected
1360	if (mCurrentTabIdx < 0) return;
1361
1362	mTabList[mCurrentTabIdx]->mButton->setLabelSelected(name);
1363	mTabList[mCurrentTabIdx]->mButton->setLabelUnselected(name);
1364}
1365
1366void LLTabContainer::selectFirstTab()
1367{
1368	selectTab( 0 );
1369}
1370
1371
1372void LLTabContainer::selectLastTab()
1373{
1374	selectTab( mTabList.size()-1 );
1375}
1376
1377void LLTabContainer::selectNextTab()
1378{
1379	BOOL tab_has_focus = FALSE;
1380	if (mCurrentTabIdx >= 0 && mTabList[mCurrentTabIdx]->mButton->hasFocus())
1381	{
1382		tab_has_focus = TRUE;
1383	}
1384	S32 idx = mCurrentTabIdx+1;
1385	if (idx >= (S32)mTabList.size())
1386		idx = 0;
1387	while (!selectTab(idx) && idx != mCurrentTabIdx)
1388	{
1389		idx = (idx + 1 ) % (S32)mTabList.size();
1390	}
1391
1392	if (tab_has_focus)
1393	{
1394		mTabList[idx]->mButton->setFocus(TRUE);
1395	}
1396}
1397
1398void LLTabContainer::selectPrevTab()
1399{
1400	BOOL tab_has_focus = FALSE;
1401	if (mCurrentTabIdx >= 0 && mTabList[mCurrentTabIdx]->mButton->hasFocus())
1402	{
1403		tab_has_focus = TRUE;
1404	}
1405	S32 idx = mCurrentTabIdx-1;
1406	if (idx < 0)
1407		idx = mTabList.size()-1;
1408	while (!selectTab(idx) && idx != mCurrentTabIdx)
1409	{
1410		idx = idx - 1;
1411		if (idx < 0)
1412			idx = mTabList.size()-1;
1413	}
1414	if (tab_has_focus)
1415	{
1416		mTabList[idx]->mButton->setFocus(TRUE);
1417	}
1418}	
1419
1420BOOL LLTabContainer::selectTabPanel(LLPanel* child)
1421{
1422	S32 idx = 0;
1423	for(tuple_list_t::iterator iter = mTabList.begin(); iter != mTabList.end(); ++iter)
1424	{
1425		LLTabTuple* tuple = *iter;
1426		if( tuple->mTabPanel == child )
1427		{
1428			return selectTab( idx );
1429		}
1430		idx++;
1431	}
1432	return FALSE;
1433}
1434
1435BOOL LLTabContainer::selectTab(S32 which)
1436{
1437	if (which >= getTabCount() || which < 0)
1438		return FALSE;
1439
1440	LLTabTuple* selected_tuple = getTab(which);
1441	if (!selected_tuple)
1442	{
1443		return FALSE;
1444	}
1445	
1446	LLSD cbdata;
1447	if (selected_tuple->mTabPanel)
1448		cbdata = selected_tuple->mTabPanel->getName();
1449
1450	BOOL res = FALSE;
1451	if( !mValidateSignal || (*mValidateSignal)( this, cbdata ) )
1452	{
1453		res = setTab(which);
1454		if (res && mCommitSignal)
1455		{
1456			(*mCommitSignal)(this, cbdata);
1457		}
1458	}
1459	
1460	return res;
1461}
1462
1463// private
1464BOOL LLTabContainer::setTab(S32 which)
1465{
1466	static LLUICachedControl<S32> tabcntr_arrow_btn_size ("UITabCntrArrowBtnSize", 0);
1467	LLTabTuple* selected_tuple = getTab(which);
1468	if (!selected_tuple)
1469	{
1470		return FALSE;
1471	}
1472
1473	BOOL is_visible = FALSE;
1474	if (selected_tuple->mButton->getEnabled())
1475	{
1476		setCurrentPanelIndex(which);
1477
1478		S32 i = 0;
1479		for(tuple_list_t::iterator iter = mTabList.begin(); iter != mTabList.end(); ++iter)
1480		{
1481			LLTabTuple* tuple = *iter;
1482			BOOL is_selected = ( tuple == selected_tuple );
1483			tuple->mButton->setUseEllipses(mUseTabEllipses);
1484			tuple->mButton->setHAlign(mFontHalign);
1485			tuple->mTabPanel->setVisible( is_selected );
1486// 			tuple->mTabPanel->setFocus(is_selected); // not clear that we want to do this here.
1487			tuple->mButton->setToggleState( is_selected );
1488			// RN: this limits tab-stops to active button only, which would require arrow keys to switch tabs
1489			tuple->mButton->setTabStop( is_selected );
1490			
1491			if (is_selected)
1492			{
1493				// Make sure selected tab is within scroll region
1494				if (mIsVertical)
1495				{
1496					S32 num_visible = getTabCount() - getMaxScrollPos();
1497					if( i >= getScrollPos() && i <= getScrollPos() + num_visible)
1498					{
1499						setCurrentPanelIndex(which);
1500						is_visible = TRUE;
1501					}
1502					else
1503					{
1504						is_visible = FALSE;
1505					}
1506				}
1507				else if (getMaxScrollPos() > 0)
1508				{
1509					if( i < getScrollPos() )
1510					{
1511						setScrollPos(i);
1512					}
1513					else
1514					{
1515						S32 available_width_with_arrows = getRect().getWidth() - mRightTabBtnOffset - 2 * (LLPANEL_BORDER_WIDTH + tabcntr_arrow_btn_size  + tabcntr_arrow_btn_size + 1);
1516						S32 running_tab_width = tuple->mButton->getRect().getWidth();
1517						S32 j = i - 1;
1518						S32 min_scroll_pos = i;
1519						if (running_tab_width < available_width_with_arrows)
1520						{
1521							while (j >= 0)
1522							{
1523								LLTabTuple* other_tuple = getTab(j);
1524								running_tab_width += other_tuple->mButton->getRect().getWidth();
1525								if (running_tab_width > available_width_with_arrows)
1526								{
1527									break;
1528								}
1529								j--;
1530							}
1531							min_scroll_pos = j + 1;
1532						}
1533						setScrollPos(llclamp(getScrollPos(), min_scroll_pos, i));
1534						setScrollPos(llmin(getScrollPos(), getMaxScrollPos()));
1535					}
1536					is_visible = TRUE;
1537				}
1538				else
1539				{
1540					is_visible = TRUE;
1541				}
1542			}
1543			i++;
1544		}
1545	}
1546	if (mIsVertical && getCurrentPanelIndex() >= 0)
1547	{
1548		LLTabTuple* tuple = getTab(getCurrentPanelIndex());
1549		tuple->mTabPanel->setVisible( TRUE );
1550		tuple->mButton->setToggleState( TRUE );
1551	}
1552	return is_visible;
1553}
1554
1555BOOL LLTabContainer::selectTabByName(const std::string& name)
1556{
1557	LLPanel* panel = getPanelByName(name);
1558	if (!panel)
1559	{
1560		llwarns << "LLTabContainer::selectTabByName("
1561			<< name << ") failed" << llendl;
1562		return FALSE;
1563	}
1564
1565	BOOL result = selectTabPanel(panel);
1566	return result;
1567}
1568
1569BOOL LLTabContainer::getTabPanelFlashing(LLPanel *child)
1570{
1571	LLTabTuple* tuple = getTabByPanel(child);
1572	if( tuple )
1573	{
1574		return tuple->mButton->getFlashing();
1575	}
1576	return FALSE;
1577}
1578
1579void LLTabContainer::setTabPanelFlashing(LLPanel* child, BOOL state )
1580{
1581	LLTabTuple* tuple = getTabByPanel(child);
1582	if( tuple )
1583	{
1584		tuple->mButton->setFlashing( state );
1585	}
1586}
1587
1588void LLTabContainer::setTabImage(LLPanel* child, std::string image_name, const LLColor4& color)
1589{
1590	LLTabTuple* tuple = getTabByPanel(child);
1591	if( tuple )
1592	{
1593		tuple->mButton->setImageOverlay(image_name, LLFontGL::LEFT, color);
1594		reshapeTuple(tuple);
1595	}
1596}
1597
1598void LLTabContainer::setTabImage(LLPanel* child, const LLUUID& image_id, const LLColor4& color)
1599{
1600	LLTabTuple* tuple = getTabByPanel(child);
1601	if( tuple )
1602	{
1603		tuple->mButton->setImageOverlay(image_id, LLFontGL::LEFT, color);
1604		reshapeTuple(tuple);
1605	}
1606}
1607
1608void LLTabContainer::setTabImage(LLPanel* child, LLIconCtrl* icon)
1609{
1610	LLTabTuple* tuple = getTabByPanel(child);
1611	LLCustomButtonIconCtrl* button;
1612
1613	if(tuple)
1614	{
1615		button = dynamic_cast<LLCustomButtonIconCtrl*>(tuple->mButton);
1616		if(button)
1617		{
1618			button->setIcon(icon);
1619			reshapeTuple(tuple);
1620		}
1621	}
1622}
1623
1624void LLTabContainer::reshapeTuple(LLTabTuple* tuple)
1625{
1626	static LLUICachedControl<S32> tab_padding ("UITabPadding", 0);
1627
1628	if (!mIsVertical)
1629	{
1630		S32 image_overlay_width = 0;
1631
1632		if(mCustomIconCtrlUsed)
1633		{
1634			LLCustomButtonIconCtrl* button = dynamic_cast<LLCustomButtonIconCtrl*>(tuple->mButton);
1635			LLIconCtrl* icon_ctrl = button ? button->getIconCtrl() : NULL;
1636			image_overlay_width = icon_ctrl ? icon_ctrl->getRect().getWidth() : 0;
1637		}
1638		else
1639		{
1640			image_overlay_width = tuple->mButton->getImageOverlay().notNull() ?
1641					tuple->mButton->getImageOverlay()->getImage()->getWidth(0) : 0;
1642		}
1643		// remove current width from total tab strip width
1644		mTotalTabWidth -= tuple->mButton->getRect().getWidth();
1645
1646		tuple->mPadding = image_overlay_width;
1647
1648		tuple->mButton->reshape(llclamp(mFont->getWidth(tuple->mButton->getLabelSelected()) + tab_padding + tuple->mPadding, mMinTabWidth, mMaxTabWidth),
1649								tuple->mButton->getRect().getHeight());
1650		// add back in button width to total tab strip width
1651		mTotalTabWidth += tuple->mButton->getRect().getWidth();
1652
1653		// tabs have changed size, might need to scroll to see current tab
1654		updateMaxScrollPos();
1655	}
1656}
1657
1658void LLTabContainer::setTitle(const std::string& title)
1659{	
1660	if (mTitleBox)
1661	{
1662		mTitleBox->setText( title );
1663	}
1664}
1665
1666const std::string LLTabContainer::getPanelTitle(S32 index)
1667{
1668	if (index >= 0 && index < (S32)mTabList.size())
1669	{
1670		LLButton* tab_button = mTabList[index]->mButton;
1671		return tab_button->getLabelSelected();
1672	}
1673	return LLStringUtil::null;
1674}
1675
1676void LLTabContainer::setTopBorderHeight(S32 height)
1677{
1678	mTopBorderHeight = height;
1679}
1680
1681S32 LLTabContainer::getTopBorderHeight() const
1682{
1683	return mTopBorderHeight;
1684}
1685
1686void LLTabContainer::setRightTabBtnOffset(S32 offset)
1687{
1688	mNextArrowBtn->translate( -offset - mRightTabBtnOffset, 0 );
1689	mRightTabBtnOffset = offset;
1690	updateMaxScrollPos();
1691}
1692
1693void LLTabContainer::setPanelTitle(S32 index, const std::string& title)
1694{
1695	static LLUICachedControl<S32> tab_padding ("UITabPadding", 0);
1696
1697	if (index >= 0 && index < getTabCount())
1698	{
1699		LLTabTuple* tuple = getTab(index);
1700		LLButton* tab_button = tuple->mButton;
1701		const LLFontGL* fontp = LLFontGL::getFontSansSerifSmall();
1702		mTotalTabWidth -= tab_button->getRect().getWidth();
1703		tab_button->reshape(llclamp(fontp->getWidth(title) + tab_padding + tuple->mPadding, mMinTabWidth, mMaxTabWidth), tab_button->getRect().getHeight());
1704		mTotalTabWidth += tab_button->getRect().getWidth();
1705		tab_button->setLabelSelected(title);
1706		tab_button->setLabelUnselected(title);
1707	}
1708	updateMaxScrollPos();
1709}
1710
1711
1712void LLTabContainer::onTabBtn( const LLSD& data, LLPanel* panel )
1713{
1714	LLTabTuple* tuple = getTabByPanel(panel);
1715	selectTabPanel( panel );
1716
1717	if (tuple)
1718	{
1719		tuple->mTabPanel->setFocus(TRUE);
1720	}
1721}
1722
1723void LLTabContainer::onNextBtn( const LLSD& data )
1724{
1725	if (!mScrolled)
1726	{
1727		scrollNext();
1728	}
1729	mScrolled = FALSE;
1730}
1731
1732void LLTabContainer::onNextBtnHeld( const LLSD& data )
1733{
1734	if (mScrollTimer.getElapsedTimeF32() > SCROLL_STEP_TIME)
1735	{
1736		mScrollTimer.reset();
1737		scrollNext();
1738		mScrolled = TRUE;
1739	}
1740}
1741
1742void LLTabContainer::onPrevBtn( const LLSD& data )
1743{
1744	if (!mScrolled)
1745	{
1746		scrollPrev();
1747	}
1748	mScrolled = FALSE;
1749}
1750
1751void LLTabContainer::onJumpFirstBtn( const LLSD& data )
1752{
1753	mScrollPos = 0;
1754}
1755
1756void LLTabContainer::onJumpLastBtn( const LLSD& data )
1757{
1758	mScrollPos = mMaxScrollPos;
1759}
1760
1761void LLTabContainer::onPrevBtnHeld( const LLSD& data )
1762{
1763	if (mScrollTimer.getElapsedTimeF32() > SCROLL_STEP_TIME)
1764	{
1765		mScrollTimer.reset();
1766		scrollPrev();
1767		mScrolled = TRUE;
1768	}
1769}
1770
1771// private
1772
1773void LLTabContainer::initButtons()
1774{
1775	// Hack:
1776	if (getRect().getHeight() == 0 || mPrevArrowBtn)
1777	{
1778		return; // Don't have a rect yet or already got called
1779	}
1780	
1781	if (mIsVertical)
1782	{
1783		static LLUICachedControl<S32> tabcntrv_arrow_btn_size ("UITabCntrvArrowBtnSize", 0);
1784		// Left and right scroll arrows (for when there are too many tabs to show all at once).
1785		S32 btn_top = getRect().getHeight();
1786		S32 btn_top_lower = getRect().mBottom+tabcntrv_arrow_btn_size;
1787
1788		LLRect up_arrow_btn_rect;
1789		up_arrow_btn_rect.setLeftTopAndSize( mMinTabWidth/2 , btn_top, tabcntrv_arrow_btn_size, tabcntrv_arrow_btn_size );
1790
1791		LLRect down_arrow_btn_rect;
1792		down_arrow_btn_rect.setLeftTopAndSize( mMinTabWidth/2 , btn_top_lower, tabcntrv_arrow_btn_size, tabcntrv_arrow_btn_size );
1793
1794		LLButton::Params prev_btn_params;
1795		prev_btn_params.name(std::string("Up Arrow"));
1796		prev_btn_params.rect(up_arrow_btn_rect);
1797		prev_btn_params.follows.flags(FOLLOWS_TOP | FOLLOWS_LEFT);
1798		prev_btn_params.image_unselected.name("scrollbutton_up_out_blue.tga");
1799		prev_btn_params.image_selected.name("scrollbutton_up_in_blue.tga");
1800		prev_btn_params.click_callback.function(boost::bind(&LLTabContainer::onPrevBtn, this, _2));
1801		mPrevArrowBtn = LLUICtrlFactory::create<LLButton>(prev_btn_params);
1802
1803		LLButton::Params next_btn_params;
1804		next_btn_params.name(std::string("Down Arrow"));
1805		next_btn_params.rect(down_arrow_btn_rect);
1806		next_btn_params.follows.flags(FOLLOWS_BOTTOM | FOLLOWS_LEFT);
1807		next_btn_params.image_unselected.name("scrollbutton_down_out_blue.tga");
1808		next_btn_params.image_selected.name("scrollbutton_down_in_blue.tga");
1809		next_btn_params.click_callback.function(boost::bind(&LLTabContainer::onNextBtn, this, _2));
1810		mNextArrowBtn = LLUICtrlFactory::create<LLButton>(next_btn_params);
1811	}
1812	else // Horizontal
1813	{
1814		static LLUICachedControl<S32> tabcntr_arrow_btn_size ("UITabCntrArrowBtnSize", 0);
1815		S32 arrow_fudge = 1;		//  match new art better 
1816
1817		// Left and right scroll arrows (for when there are too many tabs to show all at once).
1818		S32 btn_top = (getTabPosition() == TOP ) ? getRect().getHeight() - getTopBorderHeight() : tabcntr_arrow_btn_size + 1;
1819
1820		LLRect left_arrow_btn_rect;
1821		left_arrow_btn_rect.setLeftTopAndSize( LLPANEL_BORDER_WIDTH+1+tabcntr_arrow_btn_size, btn_top + arrow_fudge, tabcntr_arrow_btn_size, mTabHeight );
1822
1823		LLRect jump_left_arrow_btn_rect;
1824		jump_left_arrow_btn_rect.setLeftTopAndSize( LLPANEL_BORDER_WIDTH+1, btn_top + arrow_fudge, tabcntr_arrow_btn_size, mTabHeight );
1825
1826		S32 right_pad = tabcntr_arrow_btn_size + LLPANEL_BORDER_WIDTH + 1;
1827
1828		LLRect right_arrow_btn_rect;
1829		right_arrow_btn_rect.setLeftTopAndSize( getRect().getWidth() - mRightTabBtnOffset - right_pad - tabcntr_arrow_btn_size,
1830												btn_top + arrow_fu

Large files files are truncated, but you can click here to view the full file