PageRenderTime 128ms CodeModel.GetById 21ms app.highlight 97ms RepoModel.GetById 2ms app.codeStats 0ms

/indra/llui/lltoolbar.cpp

https://bitbucket.org/lindenlab/viewer-beta/
C++ | 1230 lines | 941 code | 183 blank | 106 comment | 161 complexity | 9f4c875848a2f4205c1f30dee542cf3a MD5 | raw file
   1/** 
   2 * @file lltoolbar.cpp
   3 * @author Richard Nelson
   4 * @brief User customizable toolbar class
   5 *
   6 * $LicenseInfo:firstyear=2011&license=viewerlgpl$
   7 * Second Life Viewer Source Code
   8 * Copyright (C) 2011, Linden Research, Inc.
   9 * 
  10 * This library is free software; you can redistribute it and/or
  11 * modify it under the terms of the GNU Lesser General Public
  12 * License as published by the Free Software Foundation;
  13 * version 2.1 of the License only.
  14 * 
  15 * This library is distributed in the hope that it will be useful,
  16 * but WITHOUT ANY WARRANTY; without even the implied warranty of
  17 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
  18 * Lesser General Public License for more details.
  19 * 
  20 * You should have received a copy of the GNU Lesser General Public
  21 * License along with this library; if not, write to the Free Software
  22 * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA  02110-1301  USA
  23 * 
  24 * Linden Research, Inc., 945 Battery Street, San Francisco, CA  94111  USA
  25 * $/LicenseInfo$
  26 */
  27
  28#include "linden_common.h"
  29
  30#include <boost/foreach.hpp>
  31#include "lltoolbar.h"
  32
  33#include "llcommandmanager.h"
  34#include "llmenugl.h"
  35#include "lltrans.h"
  36#include "llinventory.h"
  37#include "lliconctrl.h"
  38
  39// uncomment this and remove the one in llui.cpp when there is an external reference to this translation unit
  40// thanks, MSVC!
  41//static LLDefaultChildRegistry::Register<LLToolBar> r1("toolbar");
  42
  43namespace LLToolBarEnums
  44{
  45	LLLayoutStack::ELayoutOrientation getOrientation(SideType sideType)
  46	{
  47		LLLayoutStack::ELayoutOrientation orientation = LLLayoutStack::HORIZONTAL;
  48
  49		if ((sideType == SIDE_LEFT) || (sideType == SIDE_RIGHT))
  50		{
  51			orientation = LLLayoutStack::VERTICAL;
  52		}
  53
  54		return orientation;
  55	}
  56}
  57
  58using namespace LLToolBarEnums;
  59
  60
  61namespace LLInitParam
  62{
  63	void TypeValues<ButtonType>::declareValues()
  64	{
  65		declare("icons_with_text",	BTNTYPE_ICONS_WITH_TEXT);
  66		declare("icons_only",		BTNTYPE_ICONS_ONLY);
  67	}
  68
  69	void TypeValues<SideType>::declareValues()
  70	{
  71		declare("bottom",	SIDE_BOTTOM);
  72		declare("left",		SIDE_LEFT);
  73		declare("right",	SIDE_RIGHT);
  74		declare("top",		SIDE_TOP);
  75	}
  76}
  77
  78LLToolBar::Params::Params()
  79:	button_display_mode("button_display_mode"),
  80	commands("command"),
  81	side("side", SIDE_TOP),
  82	button_icon("button_icon"),
  83	button_icon_and_text("button_icon_and_text"),
  84	read_only("read_only", false),
  85	wrap("wrap", true),
  86	pad_left("pad_left"),
  87	pad_top("pad_top"),
  88	pad_right("pad_right"),
  89	pad_bottom("pad_bottom"),
  90	pad_between("pad_between"),
  91	min_girth("min_girth"),
  92	button_panel("button_panel")
  93{}
  94
  95LLToolBar::LLToolBar(const LLToolBar::Params& p)
  96:	LLUICtrl(p),
  97	mReadOnly(p.read_only),
  98	mButtonType(p.button_display_mode),
  99	mSideType(p.side),
 100	mWrap(p.wrap),
 101	mNeedsLayout(false),
 102	mModified(false),
 103	mButtonPanel(NULL),
 104	mCenteringStack(NULL),
 105	mPadLeft(p.pad_left),
 106	mPadRight(p.pad_right),
 107	mPadTop(p.pad_top),
 108	mPadBottom(p.pad_bottom),
 109	mPadBetween(p.pad_between),
 110	mMinGirth(p.min_girth),
 111	mPopupMenuHandle(),
 112	mRightMouseTargetButton(NULL),
 113	mStartDragItemCallback(NULL),
 114	mHandleDragItemCallback(NULL),
 115	mHandleDropCallback(NULL),
 116	mButtonAddSignal(NULL),
 117	mButtonEnterSignal(NULL),
 118	mButtonLeaveSignal(NULL),
 119	mButtonRemoveSignal(NULL),
 120	mDragAndDropTarget(false)
 121{
 122	mButtonParams[LLToolBarEnums::BTNTYPE_ICONS_WITH_TEXT] = p.button_icon_and_text;
 123	mButtonParams[LLToolBarEnums::BTNTYPE_ICONS_ONLY] = p.button_icon;
 124}
 125
 126LLToolBar::~LLToolBar()
 127{
 128	delete mPopupMenuHandle.get();
 129	delete mButtonAddSignal;
 130	delete mButtonEnterSignal;
 131	delete mButtonLeaveSignal;
 132	delete mButtonRemoveSignal;
 133}
 134
 135void LLToolBar::createContextMenu()
 136{
 137	if (!mPopupMenuHandle.get())
 138	{
 139		// Setup bindings specific to this instance for the context menu options
 140
 141		LLUICtrl::CommitCallbackRegistry::ScopedRegistrar commit_reg;
 142		commit_reg.add("Toolbars.EnableSetting", boost::bind(&LLToolBar::onSettingEnable, this, _2));
 143		commit_reg.add("Toolbars.RemoveSelectedCommand", boost::bind(&LLToolBar::onRemoveSelectedCommand, this));
 144
 145		LLUICtrl::EnableCallbackRegistry::ScopedRegistrar enable_reg;
 146		enable_reg.add("Toolbars.CheckSetting", boost::bind(&LLToolBar::isSettingChecked, this, _2));
 147
 148		// Create the context menu
 149		LLContextMenu* menu = LLUICtrlFactory::instance().createFromFile<LLContextMenu>("menu_toolbars.xml", LLMenuGL::sMenuContainer, LLMenuHolderGL::child_registry_t::instance());
 150
 151		if (menu)
 152		{
 153			menu->setBackgroundColor(LLUIColorTable::instance().getColor("MenuPopupBgColor"));
 154			mPopupMenuHandle = menu->getHandle();
 155			mRemoveButtonHandle = menu->getChild<LLView>("Remove button")->getHandle();
 156		}
 157		else
 158		{
 159			llwarns << "Unable to load toolbars context menu." << llendl;
 160		}
 161	}
 162	
 163	if (mRemoveButtonHandle.get())
 164	{
 165		// Disable/Enable the "Remove button" menu item depending on whether or not a button was clicked
 166		mRemoveButtonHandle.get()->setEnabled(mRightMouseTargetButton != NULL);
 167	}
 168}
 169
 170void LLToolBar::initFromParams(const LLToolBar::Params& p)
 171{
 172	// Initialize the base object
 173	LLUICtrl::initFromParams(p);
 174	
 175	LLLayoutStack::ELayoutOrientation orientation = getOrientation(p.side);
 176
 177	LLLayoutStack::Params centering_stack_p;
 178	centering_stack_p.name = "centering_stack";
 179	centering_stack_p.rect = getLocalRect();
 180	centering_stack_p.follows.flags = FOLLOWS_ALL;
 181	centering_stack_p.orientation = orientation;
 182	centering_stack_p.mouse_opaque = false;
 183
 184	mCenteringStack = LLUICtrlFactory::create<LLLayoutStack>(centering_stack_p);
 185	addChild(mCenteringStack);
 186	
 187	LLLayoutPanel::Params border_panel_p;
 188	border_panel_p.name = "border_panel";
 189	border_panel_p.rect = getLocalRect();
 190	border_panel_p.auto_resize = true;
 191	border_panel_p.user_resize = false;
 192	border_panel_p.mouse_opaque = false;
 193	
 194	mCenteringStack->addChild(LLUICtrlFactory::create<LLLayoutPanel>(border_panel_p));
 195
 196	LLLayoutPanel::Params center_panel_p;
 197	center_panel_p.name = "center_panel";
 198	center_panel_p.rect = getLocalRect();
 199	center_panel_p.auto_resize = false;
 200	center_panel_p.user_resize = false;
 201	center_panel_p.mouse_opaque = false;
 202	LLLayoutPanel* center_panel = LLUICtrlFactory::create<LLLayoutPanel>(center_panel_p);
 203	mCenteringStack->addChild(center_panel);
 204
 205	LLPanel::Params button_panel_p(p.button_panel);
 206	button_panel_p.rect = center_panel->getLocalRect();
 207		button_panel_p.follows.flags = FOLLOWS_BOTTOM|FOLLOWS_LEFT;
 208	mButtonPanel = LLUICtrlFactory::create<LLPanel>(button_panel_p);
 209	center_panel->addChild(mButtonPanel);
 210	
 211	mCenteringStack->addChild(LLUICtrlFactory::create<LLLayoutPanel>(border_panel_p));
 212
 213	BOOST_FOREACH(LLCommandId id, p.commands)
 214	{
 215		addCommand(id);
 216	}
 217
 218	mNeedsLayout = true;
 219}
 220
 221bool LLToolBar::addCommand(const LLCommandId& commandId, int rank)
 222{
 223	LLCommand * command = LLCommandManager::instance().getCommand(commandId);
 224	if (!command) return false;
 225	
 226	// Create the button and do the things that don't need ordering
 227	LLToolBarButton* button = createButton(commandId);
 228	mButtonPanel->addChild(button);
 229	mButtonMap.insert(std::make_pair(commandId.uuid(), button));
 230
 231	// Insert the command and button in the right place in their respective lists
 232	if ((rank >= mButtonCommands.size()) || (rank == RANK_NONE))
 233	{
 234		// In that case, back load
 235		mButtonCommands.push_back(command->id());
 236		mButtons.push_back(button);
 237	}
 238	else 
 239	{
 240		// Insert in place: iterate to the right spot...
 241		std::list<LLToolBarButton*>::iterator it_button = mButtons.begin();
 242		command_id_list_t::iterator it_command = mButtonCommands.begin();
 243		while (rank > 0)
 244		{
 245			++it_button;
 246			++it_command;
 247			rank--;
 248		}
 249		// ...then insert
 250		mButtonCommands.insert(it_command, command->id());
 251		mButtons.insert(it_button,button);
 252	}
 253
 254	mNeedsLayout = true;
 255
 256	updateLayoutAsNeeded();
 257
 258
 259	if (mButtonAddSignal)
 260	{
 261		(*mButtonAddSignal)(button);
 262	}
 263
 264	return true;
 265}
 266
 267// Remove a command from the list
 268// Returns the rank of the command in the original list so that doing addCommand(id,rank) right after
 269// a removeCommand(id) would leave the list unchanged.
 270// Returns RANK_NONE if the command is not found in the list
 271int LLToolBar::removeCommand(const LLCommandId& commandId)
 272{
 273	if (!hasCommand(commandId)) return RANK_NONE;
 274	
 275	// First erase the map record
 276	command_id_map::iterator it = mButtonMap.find(commandId.uuid());
 277	mButtonMap.erase(it);
 278	
 279	// Now iterate on the commands and buttons to identify the relevant records
 280	int rank = 0;
 281	std::list<LLToolBarButton*>::iterator it_button = mButtons.begin();
 282	command_id_list_t::iterator it_command = mButtonCommands.begin();
 283	while (*it_command != commandId)
 284	{
 285		++it_button;
 286		++it_command;
 287		++rank;
 288	}
 289	
 290	if (mButtonRemoveSignal)
 291	{
 292		(*mButtonRemoveSignal)(*it_button);
 293	}
 294	
 295	// Delete the button and erase the command and button records
 296	delete (*it_button);
 297	mButtonCommands.erase(it_command);
 298	mButtons.erase(it_button);
 299
 300	mNeedsLayout = true;
 301	
 302	return rank;
 303}
 304
 305void LLToolBar::clearCommandsList()
 306{
 307	// Clears the commands list
 308	mButtonCommands.clear();
 309	// This will clear the buttons
 310	createButtons();
 311}
 312
 313bool LLToolBar::hasCommand(const LLCommandId& commandId) const
 314{
 315	if (commandId != LLCommandId::null)
 316	{
 317		command_id_map::const_iterator it = mButtonMap.find(commandId.uuid());
 318		return (it != mButtonMap.end());
 319	}
 320
 321	return false;
 322}
 323
 324bool LLToolBar::enableCommand(const LLCommandId& commandId, bool enabled)
 325{
 326	LLButton * command_button = NULL;
 327	
 328	if (commandId != LLCommandId::null)
 329	{
 330		command_id_map::iterator it = mButtonMap.find(commandId.uuid());
 331		if (it != mButtonMap.end())
 332		{
 333			command_button = it->second;
 334			command_button->setEnabled(enabled);
 335		}
 336	}
 337
 338	return (command_button != NULL);
 339}
 340
 341bool LLToolBar::stopCommandInProgress(const LLCommandId& commandId)
 342{
 343	//
 344	// Note from Leslie:
 345	//
 346	// This implementation was largely put in place to handle EXP-1348 which is related to
 347	// dragging and dropping the "speak" button.  The "speak" button can be in one of two
 348	// modes, i.e., either a toggle action or a push-to-talk action.  Because of this it
 349	// responds to mouse down and mouse up in different ways, based on which behavior the
 350	// button is currently set to obey.  This was the simplest way of getting the button
 351	// to turn off the microphone for both behaviors without risking duplicate state.
 352	//
 353
 354	LLToolBarButton * command_button = NULL;
 355
 356	if (commandId != LLCommandId::null)
 357	{
 358		LLCommand* command = LLCommandManager::instance().getCommand(commandId);
 359		llassert(command);
 360
 361		// If this command has an explicit function for execution stop
 362		if (command->executeStopFunctionName().length() > 0)
 363		{
 364			command_id_map::iterator it = mButtonMap.find(commandId.uuid());
 365			if (it != mButtonMap.end())
 366			{
 367				command_button = it->second;
 368				llassert(command_button->mIsRunningSignal);
 369
 370				// Check to see if it is running
 371				if ((*command_button->mIsRunningSignal)(command_button, command->isRunningParameters()))
 372				{
 373					// Trigger an additional button commit, which calls mouse down, mouse up and commit
 374					command_button->onCommit();
 375				}
 376			}
 377		}
 378	}
 379
 380	return (command_button != NULL);
 381}
 382
 383bool LLToolBar::flashCommand(const LLCommandId& commandId, bool flash)
 384{
 385	LLButton * command_button = NULL;
 386
 387	if (commandId != LLCommandId::null)
 388	{
 389		command_id_map::iterator it = mButtonMap.find(commandId.uuid());
 390		if (it != mButtonMap.end())
 391		{
 392			command_button = it->second;
 393			command_button->setFlashing(flash ? TRUE : FALSE);
 394		}
 395	}
 396
 397	return (command_button != NULL);
 398}
 399
 400BOOL LLToolBar::handleRightMouseDown(S32 x, S32 y, MASK mask)
 401{
 402	LLRect button_panel_rect;
 403	mButtonPanel->localRectToOtherView(mButtonPanel->getLocalRect(), &button_panel_rect, this);
 404	BOOL handle_it_here = !mReadOnly && button_panel_rect.pointInRect(x, y);
 405
 406	if (handle_it_here)
 407	{
 408		// Determine which button the mouse was over during the click in case the context menu action
 409		// is intended to affect the button.
 410		mRightMouseTargetButton = NULL;
 411		BOOST_FOREACH(LLToolBarButton* button, mButtons)
 412		{
 413			LLRect button_rect;
 414			button->localRectToOtherView(button->getLocalRect(), &button_rect, this);
 415
 416			if (button_rect.pointInRect(x, y))
 417			{
 418				mRightMouseTargetButton = button;
 419				break;
 420			}
 421		}
 422
 423		createContextMenu();
 424
 425		LLContextMenu * menu = (LLContextMenu *) mPopupMenuHandle.get();
 426
 427		if (menu)
 428		{
 429			menu->show(x, y);
 430
 431			LLMenuGL::showPopup(this, menu, x, y);
 432		}
 433	}
 434
 435	return handle_it_here;
 436}
 437
 438BOOL LLToolBar::isSettingChecked(const LLSD& userdata)
 439{
 440	BOOL retval = FALSE;
 441
 442	const std::string setting_name = userdata.asString();
 443
 444	if (setting_name == "icons_with_text")
 445	{
 446		retval = (mButtonType == BTNTYPE_ICONS_WITH_TEXT);
 447	}
 448	else if (setting_name == "icons_only")
 449	{
 450		retval = (mButtonType == BTNTYPE_ICONS_ONLY);
 451	}
 452
 453	return retval;
 454}
 455
 456void LLToolBar::onSettingEnable(const LLSD& userdata)
 457{
 458	llassert(!mReadOnly);
 459
 460	const std::string setting_name = userdata.asString();
 461
 462	if (setting_name == "icons_with_text")
 463	{
 464		setButtonType(BTNTYPE_ICONS_WITH_TEXT);
 465	}
 466	else if (setting_name == "icons_only")
 467	{
 468		setButtonType(BTNTYPE_ICONS_ONLY);
 469	}
 470}
 471
 472void LLToolBar::onRemoveSelectedCommand()
 473{
 474	llassert(!mReadOnly);
 475
 476	if (mRightMouseTargetButton)
 477	{
 478		removeCommand(mRightMouseTargetButton->getCommandId());
 479
 480		mRightMouseTargetButton = NULL;
 481	}
 482}
 483
 484void LLToolBar::setButtonType(LLToolBarEnums::ButtonType button_type)
 485{
 486	bool regenerate_buttons = (mButtonType != button_type);
 487	
 488	mButtonType = button_type;
 489
 490	if (regenerate_buttons)
 491	{
 492		createButtons();
 493	}
 494}
 495
 496void LLToolBar::resizeButtonsInRow(std::vector<LLToolBarButton*>& buttons_in_row, S32 max_row_girth)
 497{
 498	// make buttons in current row all same girth
 499	BOOST_FOREACH(LLToolBarButton* button, buttons_in_row)
 500	{
 501		if (getOrientation(mSideType) == LLLayoutStack::HORIZONTAL)
 502		{
 503			button->reshape(button->mWidthRange.clamp(button->getRect().getWidth()), max_row_girth);
 504		}
 505		else // VERTICAL
 506		{
 507			button->reshape(max_row_girth, button->getRect().getHeight());
 508		}
 509	}
 510}
 511
 512// Returns the position of the coordinates as a rank in the button list. 
 513// The rank is the position a tool dropped in (x,y) would assume in the button list.
 514// The returned value is between 0 and mButtons.size(), 0 being the first element to the left
 515// (or top) and mButtons.size() the last one to the right (or bottom).
 516// Various drag data are stored in the toolbar object though are not exposed outside (and shouldn't).
 517int LLToolBar::getRankFromPosition(S32 x, S32 y)
 518{
 519	if (mButtons.empty())
 520	{
 521		return RANK_NONE;
 522	}
 523	
 524	int rank = 0;
 525
 526	// Convert the toolbar coord into button panel coords
 527	LLLayoutStack::ELayoutOrientation orientation = getOrientation(mSideType);
 528	S32 button_panel_x = 0;
 529	S32 button_panel_y = 0;
 530	localPointToOtherView(x, y, &button_panel_x, &button_panel_y, mButtonPanel);
 531	S32 dx = x - button_panel_x;
 532	S32 dy = y - button_panel_y;
 533	
 534	// Simply compare the passed coord with the buttons outbound box + padding
 535	std::list<LLToolBarButton*>::iterator it_button = mButtons.begin();
 536	std::list<LLToolBarButton*>::iterator end_button = mButtons.end();
 537	LLRect button_rect;
 538	while (it_button != end_button)
 539	{
 540		button_rect = (*it_button)->getRect();
 541		S32 point_x = button_rect.mRight + mPadRight;
 542		S32 point_y = button_rect.mBottom - mPadBottom;
 543
 544		if ((button_panel_x < point_x) && (button_panel_y > point_y))
 545		{
 546			break;
 547		}
 548		rank++;
 549		++it_button;
 550	}
 551	
 552	// Update the passed coordinates to the hit button relevant corner 
 553	// (different depending on toolbar orientation)
 554	if (rank < mButtons.size())
 555	{
 556		if (orientation == LLLayoutStack::HORIZONTAL)
 557		{
 558			// Horizontal
 559			S32 mid_point = (button_rect.mRight + button_rect.mLeft) / 2;
 560			if (button_panel_x < mid_point)
 561			{
 562				mDragx = button_rect.mLeft - mPadLeft;
 563				mDragy = button_rect.mTop + mPadTop;
 564			}
 565			else
 566			{
 567				rank++;
 568				mDragx = button_rect.mRight + mPadRight - 1;
 569				mDragy = button_rect.mTop + mPadTop;
 570			}
 571		}
 572		else
 573		{
 574			// Vertical
 575			S32 mid_point = (button_rect.mTop + button_rect.mBottom) / 2;
 576			if (button_panel_y > mid_point)
 577			{
 578				mDragx = button_rect.mLeft - mPadLeft;
 579				mDragy = button_rect.mTop + mPadTop;
 580			}
 581			else
 582			{
 583				rank++;
 584				mDragx = button_rect.mLeft - mPadLeft;
 585				mDragy = button_rect.mBottom - mPadBottom + 1;
 586			}
 587		}
 588	}
 589	else
 590	{
 591		// We hit passed the end of the list so put the insertion point at the end
 592		if (orientation == LLLayoutStack::HORIZONTAL)
 593		{
 594			mDragx = button_rect.mRight + mPadRight;
 595			mDragy = button_rect.mTop + mPadTop;
 596		}
 597		else
 598		{
 599			mDragx = button_rect.mLeft - mPadLeft;
 600			mDragy = button_rect.mBottom - mPadBottom;
 601		}
 602	}
 603
 604	// Update the "girth" of the caret, i.e. the width or height (depending of orientation)
 605	if (orientation == LLLayoutStack::HORIZONTAL)
 606	{
 607		mDragGirth = button_rect.getHeight() + mPadBottom + mPadTop;
 608	}
 609	else
 610	{
 611		mDragGirth = button_rect.getWidth() + mPadLeft + mPadRight;
 612	}
 613
 614	// The delta account for the coord model change (i.e. convert back to toolbar coord)
 615	mDragx += dx;
 616	mDragy += dy;
 617	
 618	return rank;
 619}
 620
 621int LLToolBar::getRankFromPosition(const LLCommandId& id)
 622{
 623	if (!hasCommand(id))
 624	{
 625		return RANK_NONE;
 626	}
 627	int rank = 0;
 628	std::list<LLToolBarButton*>::iterator it_button = mButtons.begin();
 629	std::list<LLToolBarButton*>::iterator end_button = mButtons.end();
 630	while (it_button != end_button)
 631	{
 632		if ((*it_button)->mId == id)
 633		{
 634			break;
 635		}
 636		rank++;
 637		++it_button;
 638	}
 639	return rank;
 640}
 641
 642void LLToolBar::updateLayoutAsNeeded()
 643{
 644	if (!mNeedsLayout) return;
 645
 646	LLLayoutStack::ELayoutOrientation orientation = getOrientation(mSideType);
 647	
 648	// our terminology for orientation-agnostic layout is such that
 649	// length refers to a distance in the direction we stack the buttons 
 650	// and girth refers to a distance in the direction buttons wrap
 651	S32 max_row_girth = 0;
 652	S32 max_row_length = 0;
 653
 654	S32 max_length;
 655	S32 max_total_girth;
 656	S32 cur_start;
 657	S32 cur_row ;
 658	S32 row_pad_start;
 659	S32 row_pad_end;
 660	S32 girth_pad_end;
 661	S32 row_running_length;
 662
 663	if (orientation == LLLayoutStack::HORIZONTAL)
 664	{
 665		max_length = getRect().getWidth() - mPadLeft - mPadRight;
 666		max_total_girth = getRect().getHeight() - mPadTop - mPadBottom;
 667		row_pad_start = mPadLeft;
 668		row_pad_end = mPadRight;
 669		cur_row = mPadTop;
 670		girth_pad_end = mPadBottom;
 671	}
 672	else // VERTICAL
 673	{
 674		max_length = getRect().getHeight() - mPadTop - mPadBottom;
 675		max_total_girth = getRect().getWidth() - mPadLeft - mPadRight;
 676		row_pad_start = mPadTop;
 677		row_pad_end = mPadBottom;
 678		cur_row = mPadLeft;
 679		girth_pad_end = mPadRight;
 680	}
 681	
 682	row_running_length = row_pad_start;
 683	cur_start = row_pad_start;
 684
 685
 686	LLRect panel_rect = mButtonPanel->getLocalRect();
 687
 688	std::vector<LLToolBarButton*> buttons_in_row;
 689
 690	BOOST_FOREACH(LLToolBarButton* button, mButtons)
 691	{
 692		button->reshape(button->mWidthRange.getMin(), button->mDesiredHeight);
 693		button->autoResize();
 694
 695		S32 button_clamped_width = button->mWidthRange.clamp(button->getRect().getWidth());
 696		S32 button_length = (orientation == LLLayoutStack::HORIZONTAL)
 697							? button_clamped_width
 698							: button->getRect().getHeight();
 699		S32 button_girth = (orientation == LLLayoutStack::HORIZONTAL)
 700							? button->getRect().getHeight()
 701							: button_clamped_width;
 702		
 703		// wrap if needed
 704		if (mWrap
 705			&& row_running_length + button_length > max_length	// out of room...
 706			&& cur_start != row_pad_start)						// ...and not first button in row
 707		{
 708			if (orientation == LLLayoutStack::VERTICAL)
 709			{	// row girth (width in this case) is clamped to allowable button widths
 710				max_row_girth = button->mWidthRange.clamp(max_row_girth);
 711			}
 712
 713			// make buttons in current row all same girth
 714			resizeButtonsInRow(buttons_in_row, max_row_girth);
 715			buttons_in_row.clear();
 716
 717			max_row_length = llmax(max_row_length, row_running_length);
 718			row_running_length = row_pad_start;
 719			cur_start = row_pad_start;
 720			cur_row += max_row_girth + mPadBetween;
 721			max_row_girth = 0;
 722		}
 723
 724		LLRect button_rect;
 725		if (orientation == LLLayoutStack::HORIZONTAL)
 726		{
 727			button_rect.setLeftTopAndSize(cur_start, panel_rect.mTop - cur_row, button_clamped_width, button->getRect().getHeight());
 728		}
 729		else // VERTICAL
 730		{
 731			button_rect.setLeftTopAndSize(cur_row, panel_rect.mTop - cur_start, button_clamped_width, button->getRect().getHeight());
 732		}
 733		button->setShape(button_rect);
 734		
 735		buttons_in_row.push_back(button);
 736
 737		row_running_length += button_length + mPadBetween;
 738		cur_start = row_running_length;
 739		max_row_girth = llmax(button_girth, max_row_girth);
 740	}
 741
 742	// final resizing in "girth" direction
 743	S32 total_girth =	cur_row				// current row position...
 744						+ max_row_girth		// ...incremented by size of final row...
 745						+ girth_pad_end;	// ...plus padding reserved on end
 746	total_girth = llmax(total_girth,mMinGirth);
 747	
 748	max_row_length = llmax(max_row_length, row_running_length - mPadBetween + row_pad_end);
 749
 750	resizeButtonsInRow(buttons_in_row, max_row_girth);
 751
 752	// grow and optionally shift toolbar to accommodate buttons
 753	if (orientation == LLLayoutStack::HORIZONTAL)
 754	{
 755		if (mSideType == SIDE_TOP)
 756		{ // shift down to maintain top edge
 757			translate(0, getRect().getHeight() - total_girth);
 758		}
 759
 760		reshape(getRect().getWidth(), total_girth);
 761		mButtonPanel->reshape(max_row_length, total_girth);
 762	}
 763	else // VERTICAL
 764	{
 765		if (mSideType == SIDE_RIGHT)
 766		{ // shift left to maintain right edge
 767			translate(getRect().getWidth() - total_girth, 0);
 768		}
 769		
 770		reshape(total_girth, getRect().getHeight());
 771		mButtonPanel->reshape(total_girth, max_row_length);
 772	}
 773
 774	// make parent fit button panel
 775	mButtonPanel->getParent()->setShape(mButtonPanel->getLocalRect());
 776
 777	// re-center toolbar buttons
 778	mCenteringStack->updateLayout();
 779
 780	if (!mButtons.empty())
 781	{
 782		mButtonPanel->setVisible(TRUE);
 783		mButtonPanel->setMouseOpaque(TRUE);
 784	}
 785
 786	// don't clear flag until after we've resized ourselves, to avoid laying out every frame
 787	mNeedsLayout = false;
 788}
 789
 790
 791void LLToolBar::draw()
 792{
 793	if (mButtons.empty())
 794	{
 795		mButtonPanel->setVisible(FALSE);
 796		mButtonPanel->setMouseOpaque(FALSE);
 797	}
 798	else
 799	{
 800		mButtonPanel->setVisible(TRUE);
 801		mButtonPanel->setMouseOpaque(TRUE);
 802	}
 803
 804	// Update enable/disable state and highlight state for editable toolbars
 805	if (!mReadOnly)
 806	{
 807		for (toolbar_button_list::iterator btn_it = mButtons.begin(); btn_it != mButtons.end(); ++btn_it)
 808		{
 809			LLToolBarButton* btn = *btn_it;
 810			LLCommand* command = LLCommandManager::instance().getCommand(btn->mId);
 811
 812			if (command && btn->mIsEnabledSignal)
 813			{
 814				const bool button_command_enabled = (*btn->mIsEnabledSignal)(btn, command->isEnabledParameters());
 815				btn->setEnabled(button_command_enabled);
 816			}
 817
 818			if (command && btn->mIsRunningSignal)
 819			{
 820				const bool button_command_running = (*btn->mIsRunningSignal)(btn, command->isRunningParameters());
 821				btn->setToggleState(button_command_running);
 822			}
 823		}
 824	}
 825
 826	updateLayoutAsNeeded();
 827	// rect may have shifted during layout
 828	LLUI::popMatrix();
 829	LLUI::pushMatrix();
 830	LLUI::translate((F32)getRect().mLeft, (F32)getRect().mBottom, 0.f);
 831
 832	// Position the caret 
 833	LLIconCtrl* caret = getChild<LLIconCtrl>("caret");
 834	caret->setVisible(FALSE);
 835	if (mDragAndDropTarget && !mButtonCommands.empty())
 836	{
 837		LLRect caret_rect = caret->getRect();
 838		LLRect toolbar_rect = getRect();
 839		if (getOrientation(mSideType) == LLLayoutStack::HORIZONTAL)
 840		{
 841			caret->setRect(LLRect(mDragx-caret_rect.getWidth()/2+1,
 842								  mDragy,
 843								  mDragx+caret_rect.getWidth()/2+1,
 844								  mDragy-mDragGirth));
 845		}
 846		else
 847		{
 848			caret->setRect(LLRect(mDragx,
 849								  mDragy+caret_rect.getHeight()/2,
 850								  mDragx+mDragGirth,
 851								  mDragy-caret_rect.getHeight()/2));
 852		}
 853		caret->setVisible(TRUE);
 854	}
 855		
 856	LLUICtrl::draw();
 857	caret->setVisible(FALSE);
 858	mDragAndDropTarget = false;
 859}
 860
 861void LLToolBar::reshape(S32 width, S32 height, BOOL called_from_parent)
 862{
 863	LLUICtrl::reshape(width, height, called_from_parent);
 864	mNeedsLayout = true;
 865}
 866
 867void LLToolBar::createButtons()
 868{
 869	BOOST_FOREACH(LLToolBarButton* button, mButtons)
 870	{
 871		if (mButtonRemoveSignal)
 872		{
 873			(*mButtonRemoveSignal)(button);
 874		}
 875		
 876		delete button;
 877	}
 878	mButtons.clear();
 879	mButtonMap.clear();
 880	mRightMouseTargetButton = NULL;
 881	
 882	BOOST_FOREACH(LLCommandId& command_id, mButtonCommands)
 883	{
 884		LLToolBarButton* button = createButton(command_id);
 885		mButtons.push_back(button);
 886		mButtonPanel->addChild(button);
 887		mButtonMap.insert(std::make_pair(command_id.uuid(), button));
 888		
 889		if (mButtonAddSignal)
 890		{
 891			(*mButtonAddSignal)(button);
 892		}
 893	}
 894	mNeedsLayout = true;
 895}
 896
 897void LLToolBarButton::callIfEnabled(LLUICtrl::commit_callback_t commit, LLUICtrl* ctrl, const LLSD& param )
 898{
 899	LLCommand* command = LLCommandManager::instance().getCommand(mId);
 900
 901	if (!mIsEnabledSignal || (*mIsEnabledSignal)(this, command->isEnabledParameters()))
 902	{
 903		commit(ctrl, param);
 904	}
 905}
 906
 907LLToolBarButton* LLToolBar::createButton(const LLCommandId& id)
 908{
 909	LLCommand* commandp = LLCommandManager::instance().getCommand(id);
 910	if (!commandp) return NULL;
 911
 912	LLToolBarButton::Params button_p;
 913	button_p.name = commandp->name();
 914	button_p.label = LLTrans::getString(commandp->labelRef());
 915	button_p.tool_tip = LLTrans::getString(commandp->tooltipRef());
 916	button_p.image_overlay = LLUI::getUIImage(commandp->icon());
 917	button_p.overwriteFrom(mButtonParams[mButtonType]);
 918	LLToolBarButton* button = LLUICtrlFactory::create<LLToolBarButton>(button_p);
 919
 920	if (!mReadOnly)
 921	{
 922		enable_callback_t isEnabledCB;
 923
 924		const std::string& isEnabledFunction = commandp->isEnabledFunctionName();
 925		if (isEnabledFunction.length() > 0)
 926		{
 927			LLUICtrl::EnableCallbackParam isEnabledParam;
 928			isEnabledParam.function_name = isEnabledFunction;
 929			isEnabledParam.parameter = commandp->isEnabledParameters();
 930			isEnabledCB = initEnableCallback(isEnabledParam);
 931
 932			if (NULL == button->mIsEnabledSignal)
 933			{
 934				button->mIsEnabledSignal = new enable_signal_t();
 935			}
 936
 937			button->mIsEnabledSignal->connect(isEnabledCB);
 938		}
 939
 940		LLUICtrl::CommitCallbackParam executeParam;
 941		executeParam.function_name = commandp->executeFunctionName();
 942		executeParam.parameter = commandp->executeParameters();
 943
 944		// If we have a "stop" function then we map the command to mouse down / mouse up otherwise commit
 945		const std::string& executeStopFunction = commandp->executeStopFunctionName();
 946		if (executeStopFunction.length() > 0)
 947		{
 948			LLUICtrl::CommitCallbackParam executeStopParam;
 949			executeStopParam.function_name = executeStopFunction;
 950			executeStopParam.parameter = commandp->executeStopParameters();
 951			LLUICtrl::commit_callback_t execute_func = initCommitCallback(executeParam);
 952			LLUICtrl::commit_callback_t stop_func = initCommitCallback(executeStopParam);
 953			
 954			button->setMouseDownCallback(boost::bind(&LLToolBarButton::callIfEnabled, button, execute_func, _1, _2));
 955			button->setMouseUpCallback(boost::bind(&LLToolBarButton::callIfEnabled, button, stop_func, _1, _2));
 956		}
 957		else
 958		{
 959			button->setCommitCallback(executeParam);
 960		}
 961
 962		// Set up "is running" query callback
 963		const std::string& isRunningFunction = commandp->isRunningFunctionName();
 964		if (isRunningFunction.length() > 0)
 965		{
 966			LLUICtrl::EnableCallbackParam isRunningParam;
 967			isRunningParam.function_name = isRunningFunction;
 968			isRunningParam.parameter = commandp->isRunningParameters();
 969			enable_signal_t::slot_type isRunningCB = initEnableCallback(isRunningParam);
 970
 971			if (NULL == button->mIsRunningSignal)
 972			{
 973				button->mIsRunningSignal = new enable_signal_t();
 974			}
 975
 976			button->mIsRunningSignal->connect(isRunningCB);
 977		}
 978	}
 979
 980	// Drag and drop behavior must work also if provided in the Toybox and, potentially, any read-only toolbar
 981	button->setStartDragCallback(mStartDragItemCallback);
 982	button->setHandleDragCallback(mHandleDragItemCallback);
 983
 984	button->setCommandId(id);
 985
 986	return button;
 987}
 988
 989boost::signals2::connection connectSignal(LLToolBar::button_signal_t*& signal, const LLToolBar::button_signal_t::slot_type& cb)
 990{
 991	if (!signal)
 992	{
 993		signal = new LLToolBar::button_signal_t();
 994	}
 995
 996	return signal->connect(cb);
 997}
 998
 999boost::signals2::connection LLToolBar::setButtonAddCallback(const button_signal_t::slot_type& cb)
1000{
1001	return connectSignal(mButtonAddSignal, cb);
1002}
1003
1004boost::signals2::connection LLToolBar::setButtonEnterCallback(const button_signal_t::slot_type& cb)
1005{
1006	return connectSignal(mButtonEnterSignal, cb);
1007}
1008
1009boost::signals2::connection LLToolBar::setButtonLeaveCallback(const button_signal_t::slot_type& cb)
1010{
1011	return connectSignal(mButtonLeaveSignal, cb);
1012}
1013
1014boost::signals2::connection LLToolBar::setButtonRemoveCallback(const button_signal_t::slot_type& cb)
1015{
1016	return connectSignal(mButtonRemoveSignal, cb);
1017}
1018
1019BOOL LLToolBar::handleDragAndDrop(S32 x, S32 y, MASK mask, BOOL drop,
1020										EDragAndDropType cargo_type,
1021										void* cargo_data,
1022										EAcceptance* accept,
1023										std::string& tooltip_msg)
1024{
1025	// If we have a drop callback, that means that we can handle the drop
1026	BOOL handled = (mHandleDropCallback ? TRUE : FALSE);
1027	
1028	// if drop is set, it's time to call the callback to get the operation done
1029	if (handled && drop)
1030	{
1031		handled = mHandleDropCallback(cargo_data, x, y ,this);
1032	}
1033	
1034	// We accept only single tool drop on toolbars
1035	*accept = (handled ? ACCEPT_YES_SINGLE : ACCEPT_NO);
1036	
1037	// We'll use that flag to change the visual aspect of the toolbar target on draw()
1038	mDragAndDropTarget = false;
1039	
1040	// Convert drag position into insert position and rank 
1041	if (!isReadOnly() && handled && !drop)
1042	{
1043		LLInventoryItem* inv_item = (LLInventoryItem*)cargo_data;
1044		LLAssetType::EType type = inv_item->getType();
1045		if (type == LLAssetType::AT_WIDGET)
1046		{
1047			LLCommandId dragged_command(inv_item->getUUID());
1048			int orig_rank = getRankFromPosition(dragged_command);
1049			mDragRank = getRankFromPosition(x, y);
1050			// Don't DaD if we're dragging a command on itself
1051			mDragAndDropTarget = ((orig_rank != RANK_NONE) && ((mDragRank == orig_rank) || ((mDragRank-1) == orig_rank)) ? false : true);
1052			//llinfos << "Merov debug : DaD, rank = " << mDragRank << ", dragged uui = " << inv_item->getUUID() << llendl; 
1053			/* Do the following if you want to animate the button itself
1054			LLCommandId dragged_command(inv_item->getUUID());
1055			removeCommand(dragged_command);
1056			addCommand(dragged_command,rank);
1057			*/
1058		}
1059		else
1060		{
1061			handled = FALSE;
1062		}
1063	}
1064	
1065	return handled;
1066}
1067
1068LLToolBarButton::LLToolBarButton(const Params& p) 
1069:	LLButton(p),
1070	mMouseDownX(0),
1071	mMouseDownY(0),
1072	mWidthRange(p.button_width),
1073	mDesiredHeight(p.desired_height),
1074	mId(""),
1075	mIsEnabledSignal(NULL),
1076	mIsRunningSignal(NULL),
1077	mIsStartingSignal(NULL),
1078	mIsDragged(false),
1079	mStartDragItemCallback(NULL),
1080	mHandleDragItemCallback(NULL),
1081	mOriginalImageSelected(p.image_selected),
1082	mOriginalImageUnselected(p.image_unselected),
1083	mOriginalImagePressed(p.image_pressed),
1084	mOriginalImagePressedSelected(p.image_pressed_selected),
1085	mOriginalLabelColor(p.label_color),
1086	mOriginalLabelColorSelected(p.label_color_selected),
1087	mOriginalImageOverlayColor(p.image_overlay_color),
1088	mOriginalImageOverlaySelectedColor(p.image_overlay_selected_color)
1089{
1090}
1091
1092LLToolBarButton::~LLToolBarButton()
1093{
1094	delete mIsEnabledSignal;
1095	delete mIsRunningSignal;
1096	delete mIsStartingSignal;
1097}
1098
1099BOOL LLToolBarButton::handleMouseDown(S32 x, S32 y, MASK mask)
1100{
1101	mMouseDownX = x;
1102	mMouseDownY = y;
1103	return LLButton::handleMouseDown(x, y, mask);
1104}
1105
1106BOOL LLToolBarButton::handleHover(S32 x, S32 y, MASK mask)
1107{
1108	BOOL handled = FALSE;
1109		
1110	S32 mouse_distance_squared = (x - mMouseDownX) * (x - mMouseDownX) + (y - mMouseDownY) * (y - mMouseDownY);
1111	S32 drag_threshold = LLUI::sSettingGroups["config"]->getS32("DragAndDropDistanceThreshold");
1112	if (mouse_distance_squared > drag_threshold * drag_threshold
1113		&& hasMouseCapture() && 
1114		mStartDragItemCallback && mHandleDragItemCallback)
1115	{
1116		if (!mIsDragged)
1117		{
1118			mStartDragItemCallback(x, y, this);
1119			mIsDragged = true;
1120			handled = TRUE;
1121		}
1122		else 
1123		{
1124			handled = mHandleDragItemCallback(x, y, mId.uuid(), LLAssetType::AT_WIDGET);
1125		}
1126	}
1127	else
1128	{
1129		handled = LLButton::handleHover(x, y, mask);
1130	}
1131
1132	return handled;
1133}
1134
1135void LLToolBarButton::onMouseEnter(S32 x, S32 y, MASK mask)
1136{
1137	LLUICtrl::onMouseEnter(x, y, mask);
1138
1139	// Always highlight toolbar buttons, even if they are disabled
1140	if (!gFocusMgr.getMouseCapture() || gFocusMgr.getMouseCapture() == this)
1141	{
1142		mNeedsHighlight = TRUE;
1143	}
1144
1145	LLToolBar* parent_toolbar = getParentByType<LLToolBar>();
1146	if (parent_toolbar && parent_toolbar->mButtonEnterSignal)
1147	{
1148		(*(parent_toolbar->mButtonEnterSignal))(this);
1149	}
1150}
1151
1152void LLToolBarButton::onMouseLeave(S32 x, S32 y, MASK mask)
1153{
1154	LLButton::onMouseLeave(x, y, mask);
1155	
1156	LLToolBar* parent_toolbar = getParentByType<LLToolBar>();
1157	if (parent_toolbar && parent_toolbar->mButtonLeaveSignal)
1158	{
1159		(*(parent_toolbar->mButtonLeaveSignal))(this);
1160	}	
1161}
1162
1163void LLToolBarButton::onMouseCaptureLost()
1164{
1165	mIsDragged = false;
1166}
1167
1168void LLToolBarButton::onCommit()
1169{
1170	LLCommand* command = LLCommandManager::instance().getCommand(mId);
1171
1172	if (!mIsEnabledSignal || (*mIsEnabledSignal)(this, command->isEnabledParameters()))
1173	{
1174		LLButton::onCommit();
1175	}
1176}
1177
1178void LLToolBarButton::reshape(S32 width, S32 height, BOOL called_from_parent)
1179{
1180	LLButton::reshape(mWidthRange.clamp(width), height, called_from_parent);
1181}
1182
1183void LLToolBarButton::setEnabled(BOOL enabled)
1184{
1185	if (enabled)
1186	{
1187		mImageSelected = mOriginalImageSelected;
1188		mImageUnselected = mOriginalImageUnselected;
1189		mImagePressed = mOriginalImagePressed;
1190		mImagePressedSelected = mOriginalImagePressedSelected;
1191		mUnselectedLabelColor = mOriginalLabelColor;
1192		mSelectedLabelColor = mOriginalLabelColorSelected;
1193		mImageOverlayColor = mOriginalImageOverlayColor;
1194		mImageOverlaySelectedColor = mOriginalImageOverlaySelectedColor;
1195	}
1196	else
1197	{
1198		mImageSelected = mImageDisabledSelected;
1199		mImageUnselected = mImageDisabled;
1200		mImagePressed = mImageDisabled;
1201		mImagePressedSelected = mImageDisabledSelected;
1202		mUnselectedLabelColor = mDisabledLabelColor;
1203		mSelectedLabelColor = mDisabledSelectedLabelColor;
1204		mImageOverlayColor = mImageOverlayDisabledColor;
1205		mImageOverlaySelectedColor = mImageOverlayDisabledColor;
1206	}
1207}
1208
1209const std::string LLToolBarButton::getToolTip() const	
1210{ 
1211	std::string tooltip;
1212
1213	if (labelIsTruncated() || getCurrentLabel().empty())
1214	{
1215		tooltip = LLTrans::getString(LLCommandManager::instance().getCommand(mId)->labelRef()) + " -- " + LLView::getToolTip();
1216	}
1217	else
1218	{
1219		tooltip = LLView::getToolTip();
1220	}
1221
1222	LLToolBar* parent_toolbar = getParentByType<LLToolBar>();
1223	if (parent_toolbar && parent_toolbar->mButtonTooltipSuffix.length() > 0)
1224	{
1225		tooltip = tooltip + "\n(" + parent_toolbar->mButtonTooltipSuffix + ")";
1226	}
1227
1228	return tooltip;
1229}
1230