summaryrefslogtreecommitdiff
path: root/indra/llui/lltoolbar.cpp
diff options
context:
space:
mode:
authorAnsariel <ansariel.hiller@phoenixviewer.com>2024-05-22 19:04:52 +0200
committerAnsariel <ansariel.hiller@phoenixviewer.com>2024-05-22 19:04:52 +0200
commit1b67dd855c41f5a0cda7ec2a68d98071986ca703 (patch)
treeab243607f74f78200787bba5b9b88f07ef1b966f /indra/llui/lltoolbar.cpp
parent6d6eabca44d08d5b97bfe3e941d2b9687c2246ea (diff)
parente1623bb276f83a43ce7a197e388720c05bdefe61 (diff)
Merge remote-tracking branch 'origin/main' into DRTVWR-600-maint-A
# Conflicts: # autobuild.xml # indra/cmake/CMakeLists.txt # indra/cmake/GoogleMock.cmake # indra/llaudio/llaudioengine_fmodstudio.cpp # indra/llaudio/llaudioengine_fmodstudio.h # indra/llaudio/lllistener_fmodstudio.cpp # indra/llaudio/lllistener_fmodstudio.h # indra/llaudio/llstreamingaudio_fmodstudio.cpp # indra/llaudio/llstreamingaudio_fmodstudio.h # indra/llcharacter/llmultigesture.cpp # indra/llcharacter/llmultigesture.h # indra/llimage/llimage.cpp # indra/llimage/llimagepng.cpp # indra/llimage/llimageworker.cpp # indra/llimage/tests/llimageworker_test.cpp # indra/llmessage/tests/llmockhttpclient.h # indra/llprimitive/llgltfmaterial.h # indra/llrender/llfontfreetype.cpp # indra/llui/llcombobox.cpp # indra/llui/llfolderview.cpp # indra/llui/llfolderviewmodel.h # indra/llui/lllineeditor.cpp # indra/llui/lllineeditor.h # indra/llui/lltextbase.cpp # indra/llui/lltextbase.h # indra/llui/lltexteditor.cpp # indra/llui/lltextvalidate.cpp # indra/llui/lltextvalidate.h # indra/llui/lluictrl.h # indra/llui/llview.cpp # indra/llwindow/llwindowmacosx.cpp # indra/newview/app_settings/settings.xml # indra/newview/llappearancemgr.cpp # indra/newview/llappearancemgr.h # indra/newview/llavatarpropertiesprocessor.cpp # indra/newview/llavatarpropertiesprocessor.h # indra/newview/llbreadcrumbview.cpp # indra/newview/llbreadcrumbview.h # indra/newview/llbreastmotion.cpp # indra/newview/llbreastmotion.h # indra/newview/llconversationmodel.h # indra/newview/lldensityctrl.cpp # indra/newview/lldensityctrl.h # indra/newview/llface.inl # indra/newview/llfloatereditsky.cpp # indra/newview/llfloatereditwater.cpp # indra/newview/llfloateremojipicker.h # indra/newview/llfloaterimsessiontab.cpp # indra/newview/llfloaterprofiletexture.cpp # indra/newview/llfloaterprofiletexture.h # indra/newview/llgesturemgr.cpp # indra/newview/llgesturemgr.h # indra/newview/llimpanel.cpp # indra/newview/llimpanel.h # indra/newview/llinventorybridge.cpp # indra/newview/llinventorybridge.h # indra/newview/llinventoryclipboard.cpp # indra/newview/llinventoryclipboard.h # indra/newview/llinventoryfunctions.cpp # indra/newview/llinventoryfunctions.h # indra/newview/llinventorygallery.cpp # indra/newview/lllistbrowser.cpp # indra/newview/lllistbrowser.h # indra/newview/llpanelobjectinventory.cpp # indra/newview/llpanelprofile.cpp # indra/newview/llpanelprofile.h # indra/newview/llpreviewgesture.cpp # indra/newview/llsavedsettingsglue.cpp # indra/newview/llsavedsettingsglue.h # indra/newview/lltooldraganddrop.cpp # indra/newview/llurllineeditorctrl.cpp # indra/newview/llvectorperfoptions.cpp # indra/newview/llvectorperfoptions.h # indra/newview/llviewerparceloverlay.cpp # indra/newview/llviewertexlayer.cpp # indra/newview/llviewertexturelist.cpp # indra/newview/macmain.h # indra/test/test.cpp
Diffstat (limited to 'indra/llui/lltoolbar.cpp')
-rw-r--r--indra/llui/lltoolbar.cpp2533
1 files changed, 1266 insertions, 1267 deletions
diff --git a/indra/llui/lltoolbar.cpp b/indra/llui/lltoolbar.cpp
index 1c1414dd74..9753881dfc 100644
--- a/indra/llui/lltoolbar.cpp
+++ b/indra/llui/lltoolbar.cpp
@@ -1,1267 +1,1266 @@
-/**
- * @file lltoolbar.cpp
- * @author Richard Nelson
- * @brief User customizable toolbar class
- *
- * $LicenseInfo:firstyear=2011&license=viewerlgpl$
- * Second Life Viewer Source Code
- * Copyright (C) 2011, Linden Research, Inc.
- *
- * This library is free software; you can redistribute it and/or
- * modify it under the terms of the GNU Lesser General Public
- * License as published by the Free Software Foundation;
- * version 2.1 of the License only.
- *
- * This library is distributed in the hope that it will be useful,
- * but WITHOUT ANY WARRANTY; without even the implied warranty of
- * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
- * Lesser General Public License for more details.
- *
- * You should have received a copy of the GNU Lesser General Public
- * License along with this library; if not, write to the Free Software
- * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA
- *
- * Linden Research, Inc., 945 Battery Street, San Francisco, CA 94111 USA
- * $/LicenseInfo$
- */
-
-#include "linden_common.h"
-
-#include "lltoolbar.h"
-
-#include "llcommandmanager.h"
-#include "llmenugl.h"
-#include "lltrans.h"
-#include "llinventory.h"
-#include "lliconctrl.h"
-
-// uncomment this and remove the one in llui.cpp when there is an external reference to this translation unit
-// thanks, MSVC!
-//static LLDefaultChildRegistry::Register<LLToolBar> r1("toolbar");
-
-namespace LLToolBarEnums
-{
- LLView::EOrientation getOrientation(SideType sideType)
- {
- LLView::EOrientation orientation = LLLayoutStack::HORIZONTAL;
-
- if ((sideType == SIDE_LEFT) || (sideType == SIDE_RIGHT))
- {
- orientation = LLLayoutStack::VERTICAL;
- }
-
- return orientation;
- }
-}
-
-using namespace LLToolBarEnums;
-
-
-namespace LLInitParam
-{
- void TypeValues<ButtonType>::declareValues()
- {
- declare("icons_with_text", BTNTYPE_ICONS_WITH_TEXT);
- declare("icons_only", BTNTYPE_ICONS_ONLY);
- }
-
- void TypeValues<SideType>::declareValues()
- {
- declare("bottom", SIDE_BOTTOM);
- declare("left", SIDE_LEFT);
- declare("right", SIDE_RIGHT);
- declare("top", SIDE_TOP);
- }
-}
-
-LLToolBar::Params::Params()
-: button_display_mode("button_display_mode"),
- commands("command"),
- side("side", SIDE_TOP),
- button_icon("button_icon"),
- button_icon_and_text("button_icon_and_text"),
- read_only("read_only", false),
- wrap("wrap", true),
- pad_left("pad_left"),
- pad_top("pad_top"),
- pad_right("pad_right"),
- pad_bottom("pad_bottom"),
- pad_between("pad_between"),
- min_girth("min_girth"),
- button_panel("button_panel")
-{}
-
-LLToolBar::LLToolBar(const LLToolBar::Params& p)
-: LLUICtrl(p),
- mReadOnly(p.read_only),
- mButtonType(p.button_display_mode),
- mSideType(p.side),
- mWrap(p.wrap),
- mNeedsLayout(false),
- mModified(false),
- mButtonPanel(NULL),
- mCenteringStack(NULL),
- mPadLeft(p.pad_left),
- mPadRight(p.pad_right),
- mPadTop(p.pad_top),
- mPadBottom(p.pad_bottom),
- mPadBetween(p.pad_between),
- mMinGirth(p.min_girth),
- mPopupMenuHandle(),
- mRightMouseTargetButton(NULL),
- mStartDragItemCallback(NULL),
- mHandleDragItemCallback(NULL),
- mHandleDropCallback(NULL),
- mButtonAddSignal(NULL),
- mButtonEnterSignal(NULL),
- mButtonLeaveSignal(NULL),
- mButtonRemoveSignal(NULL),
- mDragAndDropTarget(false),
- mCaretIcon(NULL),
- mCenterPanel(NULL)
-{
- mButtonParams[LLToolBarEnums::BTNTYPE_ICONS_WITH_TEXT] = p.button_icon_and_text;
- mButtonParams[LLToolBarEnums::BTNTYPE_ICONS_ONLY] = p.button_icon;
-}
-
-LLToolBar::~LLToolBar()
-{
- auto menu = mPopupMenuHandle.get();
- if (menu)
- {
- menu->die();
- mPopupMenuHandle.markDead();
- }
- delete mButtonAddSignal;
- delete mButtonEnterSignal;
- delete mButtonLeaveSignal;
- delete mButtonRemoveSignal;
-}
-
-void LLToolBar::createContextMenu()
-{
- if (!mPopupMenuHandle.get())
- {
- // Setup bindings specific to this instance for the context menu options
-
- LLUICtrl::CommitCallbackRegistry::ScopedRegistrar commit_reg;
- commit_reg.add("Toolbars.EnableSetting", boost::bind(&LLToolBar::onSettingEnable, this, _2));
- commit_reg.add("Toolbars.RemoveSelectedCommand", boost::bind(&LLToolBar::onRemoveSelectedCommand, this));
-
- LLUICtrl::EnableCallbackRegistry::ScopedRegistrar enable_reg;
- enable_reg.add("Toolbars.CheckSetting", boost::bind(&LLToolBar::isSettingChecked, this, _2));
-
- // Create the context menu
- llassert(LLMenuGL::sMenuContainer != NULL);
- LLContextMenu* menu = LLUICtrlFactory::instance().createFromFile<LLContextMenu>("menu_toolbars.xml", LLMenuGL::sMenuContainer, LLMenuHolderGL::child_registry_t::instance());
-
- if (menu)
- {
- menu->setBackgroundColor(LLUIColorTable::instance().getColor("MenuPopupBgColor"));
- mPopupMenuHandle = menu->getHandle();
- mRemoveButtonHandle = menu->getChild<LLView>("Remove button")->getHandle();
- }
- else
- {
- LL_WARNS() << "Unable to load toolbars context menu." << LL_ENDL;
- }
- }
-
- if (mRemoveButtonHandle.get())
- {
- // Disable/Enable the "Remove button" menu item depending on whether or not a button was clicked
- mRemoveButtonHandle.get()->setEnabled(mRightMouseTargetButton != NULL);
- }
-}
-
-void LLToolBar::initFromParams(const LLToolBar::Params& p)
-{
- // Initialize the base object
- LLUICtrl::initFromParams(p);
-
- LLView::EOrientation orientation = getOrientation(p.side);
-
- LLLayoutStack::Params centering_stack_p;
- centering_stack_p.name = "centering_stack";
- centering_stack_p.rect = getLocalRect();
- centering_stack_p.follows.flags = FOLLOWS_ALL;
- centering_stack_p.orientation = orientation;
- centering_stack_p.mouse_opaque = false;
-
- mCenteringStack = LLUICtrlFactory::create<LLLayoutStack>(centering_stack_p);
- addChild(mCenteringStack);
-
- LLLayoutPanel::Params border_panel_p;
- border_panel_p.name = "border_panel";
- border_panel_p.rect = getLocalRect();
- border_panel_p.auto_resize = true;
- border_panel_p.user_resize = false;
- border_panel_p.mouse_opaque = false;
-
- mCenteringStack->addChild(LLUICtrlFactory::create<LLLayoutPanel>(border_panel_p));
-
- LLLayoutPanel::Params center_panel_p;
- center_panel_p.name = "center_panel";
- center_panel_p.rect = getLocalRect();
- center_panel_p.auto_resize = false;
- center_panel_p.user_resize = false;
- center_panel_p.mouse_opaque = false;
- mCenterPanel = LLUICtrlFactory::create<LLCenterLayoutPanel>(center_panel_p);
- mCenteringStack->addChild(mCenterPanel);
-
- LLPanel::Params button_panel_p(p.button_panel);
- button_panel_p.rect = mCenterPanel->getLocalRect();
- button_panel_p.follows.flags = FOLLOWS_BOTTOM|FOLLOWS_LEFT;
- mButtonPanel = LLUICtrlFactory::create<LLPanel>(button_panel_p);
- mCenterPanel->setButtonPanel(mButtonPanel);
- mCenterPanel->addChild(mButtonPanel);
-
- mCenteringStack->addChild(LLUICtrlFactory::create<LLLayoutPanel>(border_panel_p));
-
- for (const auto& id : p.commands)
- {
- addCommand(id);
- }
-
- mNeedsLayout = true;
-}
-
-bool LLToolBar::addCommand(const LLCommandId& commandId, int rank)
-{
- LLCommand * command = LLCommandManager::instance().getCommand(commandId);
- if (!command) return false;
-
- // Create the button and do the things that don't need ordering
- LLToolBarButton* button = createButton(commandId);
- mButtonPanel->addChild(button);
- mButtonMap.insert(std::make_pair(commandId.uuid(), button));
-
- // Insert the command and button in the right place in their respective lists
- if ((rank >= mButtonCommands.size()) || (rank == RANK_NONE))
- {
- // In that case, back load
- mButtonCommands.push_back(command->id());
- mButtons.push_back(button);
- }
- else
- {
- // Insert in place: iterate to the right spot...
- std::list<LLToolBarButton*>::iterator it_button = mButtons.begin();
- command_id_list_t::iterator it_command = mButtonCommands.begin();
- while (rank > 0)
- {
- ++it_button;
- ++it_command;
- rank--;
- }
- // ...then insert
- mButtonCommands.insert(it_command, command->id());
- mButtons.insert(it_button,button);
- }
-
- mNeedsLayout = true;
-
- updateLayoutAsNeeded();
-
-
- if (mButtonAddSignal)
- {
- (*mButtonAddSignal)(button);
- }
-
- return true;
-}
-
-// Remove a command from the list
-// Returns the rank of the command in the original list so that doing addCommand(id,rank) right after
-// a removeCommand(id) would leave the list unchanged.
-// Returns RANK_NONE if the command is not found in the list
-int LLToolBar::removeCommand(const LLCommandId& commandId)
-{
- if (!hasCommand(commandId)) return RANK_NONE;
-
- // First erase the map record
- command_id_map::iterator it = mButtonMap.find(commandId.uuid());
- mButtonMap.erase(it);
-
- // Now iterate on the commands and buttons to identify the relevant records
- int rank = 0;
- std::list<LLToolBarButton*>::iterator it_button = mButtons.begin();
- command_id_list_t::iterator it_command = mButtonCommands.begin();
- while (*it_command != commandId)
- {
- ++it_button;
- ++it_command;
- ++rank;
- }
-
- if (mButtonRemoveSignal)
- {
- (*mButtonRemoveSignal)(*it_button);
- }
-
- // Delete the button and erase the command and button records
- delete (*it_button);
- mButtonCommands.erase(it_command);
- mButtons.erase(it_button);
-
- mNeedsLayout = true;
-
- return rank;
-}
-
-void LLToolBar::clearCommandsList()
-{
- // Clears the commands list
- mButtonCommands.clear();
- // This will clear the buttons
- createButtons();
-}
-
-bool LLToolBar::hasCommand(const LLCommandId& commandId) const
-{
- if (commandId != LLCommandId::null)
- {
- command_id_map::const_iterator it = mButtonMap.find(commandId.uuid());
- return (it != mButtonMap.end());
- }
-
- return false;
-}
-
-bool LLToolBar::enableCommand(const LLCommandId& commandId, bool enabled)
-{
- LLButton * command_button = NULL;
-
- if (commandId != LLCommandId::null)
- {
- command_id_map::iterator it = mButtonMap.find(commandId.uuid());
- if (it != mButtonMap.end())
- {
- command_button = it->second;
- command_button->setEnabled(enabled);
- }
- }
-
- return (command_button != NULL);
-}
-
-bool LLToolBar::stopCommandInProgress(const LLCommandId& commandId)
-{
- //
- // Note from Leslie:
- //
- // This implementation was largely put in place to handle EXP-1348 which is related to
- // dragging and dropping the "speak" button. The "speak" button can be in one of two
- // modes, i.e., either a toggle action or a push-to-talk action. Because of this it
- // responds to mouse down and mouse up in different ways, based on which behavior the
- // button is currently set to obey. This was the simplest way of getting the button
- // to turn off the microphone for both behaviors without risking duplicate state.
- //
-
- LLToolBarButton * command_button = NULL;
-
- if (commandId != LLCommandId::null)
- {
- LLCommand* command = LLCommandManager::instance().getCommand(commandId);
- llassert(command);
-
- // If this command has an explicit function for execution stop
- if (command->executeStopFunctionName().length() > 0)
- {
- command_id_map::iterator it = mButtonMap.find(commandId.uuid());
- if (it != mButtonMap.end())
- {
- command_button = it->second;
- llassert(command_button->mIsRunningSignal);
-
- // Check to see if it is running
- if ((*command_button->mIsRunningSignal)(command_button, command->isRunningParameters()))
- {
- // Trigger an additional button commit, which calls mouse down, mouse up and commit
- command_button->onCommit();
- }
- }
- }
- }
-
- return (command_button != NULL);
-}
-
-bool LLToolBar::flashCommand(const LLCommandId& commandId, bool flash, bool force_flashing/* = false */)
-{
- LLButton * command_button = NULL;
-
- if (commandId != LLCommandId::null)
- {
- command_id_map::iterator it = mButtonMap.find(commandId.uuid());
- if (it != mButtonMap.end())
- {
- command_button = it->second;
- command_button->setFlashing((bool)(flash),(bool)(force_flashing));
- }
- }
-
- return (command_button != NULL);
-}
-
-bool LLToolBar::handleRightMouseDown(S32 x, S32 y, MASK mask)
-{
- LLRect button_panel_rect;
- mButtonPanel->localRectToOtherView(mButtonPanel->getLocalRect(), &button_panel_rect, this);
- bool handle_it_here = !mReadOnly && button_panel_rect.pointInRect(x, y);
-
- if (handle_it_here)
- {
- // Determine which button the mouse was over during the click in case the context menu action
- // is intended to affect the button.
- mRightMouseTargetButton = NULL;
- for (LLToolBarButton* button : mButtons)
- {
- LLRect button_rect;
- button->localRectToOtherView(button->getLocalRect(), &button_rect, this);
-
- if (button_rect.pointInRect(x, y))
- {
- mRightMouseTargetButton = button;
- break;
- }
- }
-
- createContextMenu();
-
- LLContextMenu * menu = (LLContextMenu *) mPopupMenuHandle.get();
-
- if (menu)
- {
- menu->show(x, y);
-
- LLMenuGL::showPopup(this, menu, x, y);
- }
- }
-
- return handle_it_here;
-}
-
-bool LLToolBar::isSettingChecked(const LLSD& userdata)
-{
- bool retval = false;
-
- const std::string setting_name = userdata.asString();
-
- if (setting_name == "icons_with_text")
- {
- retval = (mButtonType == BTNTYPE_ICONS_WITH_TEXT);
- }
- else if (setting_name == "icons_only")
- {
- retval = (mButtonType == BTNTYPE_ICONS_ONLY);
- }
-
- return retval;
-}
-
-void LLToolBar::onSettingEnable(const LLSD& userdata)
-{
- llassert(!mReadOnly);
-
- const std::string setting_name = userdata.asString();
-
- if (setting_name == "icons_with_text")
- {
- setButtonType(BTNTYPE_ICONS_WITH_TEXT);
- }
- else if (setting_name == "icons_only")
- {
- setButtonType(BTNTYPE_ICONS_ONLY);
- }
-}
-
-void LLToolBar::onRemoveSelectedCommand()
-{
- llassert(!mReadOnly);
-
- if (mRightMouseTargetButton)
- {
- removeCommand(mRightMouseTargetButton->getCommandId());
-
- mRightMouseTargetButton = NULL;
- }
-}
-
-void LLToolBar::setButtonType(LLToolBarEnums::ButtonType button_type)
-{
- bool regenerate_buttons = (mButtonType != button_type);
-
- mButtonType = button_type;
-
- if (regenerate_buttons)
- {
- createButtons();
- }
-}
-
-void LLToolBar::resizeButtonsInRow(std::vector<LLToolBarButton*>& buttons_in_row, S32 max_row_girth)
-{
- // make buttons in current row all same girth
- for (LLToolBarButton* button : buttons_in_row)
- {
- if (getOrientation(mSideType) == LLLayoutStack::HORIZONTAL)
- {
- button->reshape(button->mWidthRange.clamp(button->getRect().getWidth()), max_row_girth);
- }
- else // VERTICAL
- {
- button->reshape(max_row_girth, button->getRect().getHeight());
- }
- }
-}
-
-// Returns the position of the coordinates as a rank in the button list.
-// The rank is the position a tool dropped in (x,y) would assume in the button list.
-// The returned value is between 0 and mButtons.size(), 0 being the first element to the left
-// (or top) and mButtons.size() the last one to the right (or bottom).
-// Various drag data are stored in the toolbar object though are not exposed outside (and shouldn't).
-int LLToolBar::getRankFromPosition(S32 x, S32 y)
-{
- if (mButtons.empty())
- {
- return RANK_NONE;
- }
-
- int rank = 0;
-
- // Convert the toolbar coord into button panel coords
- LLView::EOrientation orientation = getOrientation(mSideType);
- S32 button_panel_x = 0;
- S32 button_panel_y = 0;
- localPointToOtherView(x, y, &button_panel_x, &button_panel_y, mButtonPanel);
- S32 dx = x - button_panel_x;
- S32 dy = y - button_panel_y;
-
- // Simply compare the passed coord with the buttons outbound box + padding
- std::list<LLToolBarButton*>::iterator it_button = mButtons.begin();
- std::list<LLToolBarButton*>::iterator end_button = mButtons.end();
- LLRect button_rect;
- while (it_button != end_button)
- {
- button_rect = (*it_button)->getRect();
- S32 point_x = button_rect.mRight + mPadRight;
- S32 point_y = button_rect.mBottom - mPadBottom;
-
- if ((button_panel_x < point_x) && (button_panel_y > point_y))
- {
- break;
- }
- rank++;
- ++it_button;
- }
-
- // Update the passed coordinates to the hit button relevant corner
- // (different depending on toolbar orientation)
- if (rank < mButtons.size())
- {
- if (orientation == LLLayoutStack::HORIZONTAL)
- {
- // Horizontal
- S32 mid_point = (button_rect.mRight + button_rect.mLeft) / 2;
- if (button_panel_x < mid_point)
- {
- mDragx = button_rect.mLeft - mPadLeft;
- mDragy = button_rect.mTop + mPadTop;
- }
- else
- {
- rank++;
- mDragx = button_rect.mRight + mPadRight - 1;
- mDragy = button_rect.mTop + mPadTop;
- }
- }
- else
- {
- // Vertical
- S32 mid_point = (button_rect.mTop + button_rect.mBottom) / 2;
- if (button_panel_y > mid_point)
- {
- mDragx = button_rect.mLeft - mPadLeft;
- mDragy = button_rect.mTop + mPadTop;
- }
- else
- {
- rank++;
- mDragx = button_rect.mLeft - mPadLeft;
- mDragy = button_rect.mBottom - mPadBottom + 1;
- }
- }
- }
- else
- {
- // We hit passed the end of the list so put the insertion point at the end
- if (orientation == LLLayoutStack::HORIZONTAL)
- {
- mDragx = button_rect.mRight + mPadRight;
- mDragy = button_rect.mTop + mPadTop;
- }
- else
- {
- mDragx = button_rect.mLeft - mPadLeft;
- mDragy = button_rect.mBottom - mPadBottom;
- }
- }
-
- // Update the "girth" of the caret, i.e. the width or height (depending of orientation)
- if (orientation == LLLayoutStack::HORIZONTAL)
- {
- mDragGirth = button_rect.getHeight() + mPadBottom + mPadTop;
- }
- else
- {
- mDragGirth = button_rect.getWidth() + mPadLeft + mPadRight;
- }
-
- // The delta account for the coord model change (i.e. convert back to toolbar coord)
- mDragx += dx;
- mDragy += dy;
-
- return rank;
-}
-
-int LLToolBar::getRankFromPosition(const LLCommandId& id)
-{
- if (!hasCommand(id))
- {
- return RANK_NONE;
- }
- int rank = 0;
- std::list<LLToolBarButton*>::iterator it_button = mButtons.begin();
- std::list<LLToolBarButton*>::iterator end_button = mButtons.end();
- while (it_button != end_button)
- {
- if ((*it_button)->mId == id)
- {
- break;
- }
- rank++;
- ++it_button;
- }
- return rank;
-}
-
-void LLToolBar::updateLayoutAsNeeded()
-{
- if (!mNeedsLayout) return;
-
- LLView::EOrientation orientation = getOrientation(mSideType);
-
- // our terminology for orientation-agnostic layout is such that
- // length refers to a distance in the direction we stack the buttons
- // and girth refers to a distance in the direction buttons wrap
- S32 max_row_girth = 0;
- S32 max_row_length = 0;
-
- S32 max_length;
- S32 cur_start;
- S32 cur_row ;
- S32 row_pad_start;
- S32 row_pad_end;
- S32 girth_pad_end;
- S32 row_running_length;
-
- if (orientation == LLLayoutStack::HORIZONTAL)
- {
- max_length = getRect().getWidth() - mPadLeft - mPadRight;
- row_pad_start = mPadLeft;
- row_pad_end = mPadRight;
- cur_row = mPadTop;
- girth_pad_end = mPadBottom;
- }
- else // VERTICAL
- {
- max_length = getRect().getHeight() - mPadTop - mPadBottom;
- row_pad_start = mPadTop;
- row_pad_end = mPadBottom;
- cur_row = mPadLeft;
- girth_pad_end = mPadRight;
- }
-
- row_running_length = row_pad_start;
- cur_start = row_pad_start;
-
-
- LLRect panel_rect = mButtonPanel->getLocalRect();
-
- std::vector<LLToolBarButton*> buttons_in_row;
-
- for (LLToolBarButton* button : mButtons)
- {
- button->reshape(button->mWidthRange.getMin(), button->mDesiredHeight);
- button->autoResize();
-
- S32 button_clamped_width = button->mWidthRange.clamp(button->getRect().getWidth());
- S32 button_length = (orientation == LLLayoutStack::HORIZONTAL)
- ? button_clamped_width
- : button->getRect().getHeight();
- S32 button_girth = (orientation == LLLayoutStack::HORIZONTAL)
- ? button->getRect().getHeight()
- : button_clamped_width;
-
- // wrap if needed
- if (mWrap
- && row_running_length + button_length > max_length // out of room...
- && cur_start != row_pad_start) // ...and not first button in row
- {
- if (orientation == LLLayoutStack::VERTICAL)
- { // row girth (width in this case) is clamped to allowable button widths
- max_row_girth = button->mWidthRange.clamp(max_row_girth);
- }
-
- // make buttons in current row all same girth
- resizeButtonsInRow(buttons_in_row, max_row_girth);
- buttons_in_row.clear();
-
- max_row_length = llmax(max_row_length, row_running_length);
- row_running_length = row_pad_start;
- cur_start = row_pad_start;
- cur_row += max_row_girth + mPadBetween;
- max_row_girth = 0;
- }
-
- LLRect button_rect;
- if (orientation == LLLayoutStack::HORIZONTAL)
- {
- button_rect.setLeftTopAndSize(cur_start, panel_rect.mTop - cur_row, button_clamped_width, button->getRect().getHeight());
- }
- else // VERTICAL
- {
- button_rect.setLeftTopAndSize(cur_row, panel_rect.mTop - cur_start, button_clamped_width, button->getRect().getHeight());
- }
- button->setShape(button_rect);
-
- buttons_in_row.push_back(button);
-
- row_running_length += button_length + mPadBetween;
- cur_start = row_running_length;
- max_row_girth = llmax(button_girth, max_row_girth);
- }
-
- // final resizing in "girth" direction
- S32 total_girth = cur_row // current row position...
- + max_row_girth // ...incremented by size of final row...
- + girth_pad_end; // ...plus padding reserved on end
- total_girth = llmax(total_girth,mMinGirth);
-
- max_row_length = llmax(max_row_length, row_running_length - mPadBetween + row_pad_end);
-
- resizeButtonsInRow(buttons_in_row, max_row_girth);
-
- // grow and optionally shift toolbar to accommodate buttons
- if (orientation == LLLayoutStack::HORIZONTAL)
- {
- if (mSideType == SIDE_TOP)
- { // shift down to maintain top edge
- translate(0, getRect().getHeight() - total_girth);
- }
-
- reshape(getRect().getWidth(), total_girth);
- mButtonPanel->reshape(max_row_length, total_girth);
- }
- else // VERTICAL
- {
- if (mSideType == SIDE_RIGHT)
- { // shift left to maintain right edge
- translate(getRect().getWidth() - total_girth, 0);
- }
-
- reshape(total_girth, getRect().getHeight());
- mButtonPanel->reshape(total_girth, max_row_length);
- }
-
- // make parent fit button panel
- mButtonPanel->getParent()->setShape(mButtonPanel->getLocalRect());
-
- // re-center toolbar buttons
- mCenteringStack->updateLayout();
-
- if (!mButtons.empty())
- {
- mButtonPanel->setVisible(true);
- mButtonPanel->setMouseOpaque(true);
- }
-
- // don't clear flag until after we've resized ourselves, to avoid laying out every frame
- mNeedsLayout = false;
-}
-
-
-void LLToolBar::draw()
-{
- if (mButtons.empty())
- {
- mButtonPanel->setVisible(false);
- mButtonPanel->setMouseOpaque(false);
- }
- else
- {
- mButtonPanel->setVisible(true);
- mButtonPanel->setMouseOpaque(true);
- }
-
- // Update enable/disable state and highlight state for editable toolbars
- if (!mReadOnly)
- {
- for (toolbar_button_list::iterator btn_it = mButtons.begin(); btn_it != mButtons.end(); ++btn_it)
- {
- LLToolBarButton* btn = *btn_it;
- LLCommand* command = LLCommandManager::instance().getCommand(btn->mId);
-
- if (command && btn->mIsEnabledSignal)
- {
- const bool button_command_enabled = (*btn->mIsEnabledSignal)(btn, command->isEnabledParameters());
- btn->setEnabled(button_command_enabled);
- }
-
- if (command && btn->mIsRunningSignal)
- {
- const bool button_command_running = (*btn->mIsRunningSignal)(btn, command->isRunningParameters());
- btn->setToggleState(button_command_running);
- }
- }
- }
-
- updateLayoutAsNeeded();
- // rect may have shifted during layout
- LLUI::popMatrix();
- LLUI::pushMatrix();
- LLUI::translate((F32)getRect().mLeft, (F32)getRect().mBottom);
-
- // Position the caret
- if (!mCaretIcon)
- {
- mCaretIcon = getChild<LLIconCtrl>("caret");
- }
-
- LLIconCtrl* caret = mCaretIcon;
- caret->setVisible(false);
- if (mDragAndDropTarget && !mButtonCommands.empty())
- {
- LLRect caret_rect = caret->getRect();
- if (getOrientation(mSideType) == LLLayoutStack::HORIZONTAL)
- {
- caret->setRect(LLRect(mDragx-caret_rect.getWidth()/2+1,
- mDragy,
- mDragx+caret_rect.getWidth()/2+1,
- mDragy-mDragGirth));
- }
- else
- {
- caret->setRect(LLRect(mDragx,
- mDragy+caret_rect.getHeight()/2,
- mDragx+mDragGirth,
- mDragy-caret_rect.getHeight()/2));
- }
- caret->setVisible(true);
- }
-
- LLUICtrl::draw();
- caret->setVisible(false);
- mDragAndDropTarget = false;
-}
-
-void LLToolBar::reshape(S32 width, S32 height, bool called_from_parent)
-{
- LLUICtrl::reshape(width, height, called_from_parent);
- mNeedsLayout = true;
-}
-
-void LLToolBar::createButtons()
-{
- std::set<LLUUID> set_flashing;
-
- for (LLToolBarButton* button : mButtons)
- {
- if (button->getFlashTimer() && button->getFlashTimer()->isFlashingInProgress())
- {
- set_flashing.insert(button->getCommandId().uuid());
- }
-
- if (mButtonRemoveSignal)
- {
- (*mButtonRemoveSignal)(button);
- }
-
- delete button;
- }
- mButtons.clear();
- mButtonMap.clear();
- mRightMouseTargetButton = NULL;
-
- for (const LLCommandId& command_id : mButtonCommands)
- {
- LLToolBarButton* button = createButton(command_id);
- mButtons.push_back(button);
- mButtonPanel->addChild(button);
- mButtonMap.insert(std::make_pair(command_id.uuid(), button));
-
- if (mButtonAddSignal)
- {
- (*mButtonAddSignal)(button);
- }
-
- if (set_flashing.find(button->getCommandId().uuid()) != set_flashing.end())
- {
- button->setFlashing(true);
- }
- }
- mNeedsLayout = true;
-}
-
-void LLToolBarButton::callIfEnabled(LLUICtrl::commit_callback_t commit, LLUICtrl* ctrl, const LLSD& param )
-{
- LLCommand* command = LLCommandManager::instance().getCommand(mId);
-
- if (!mIsEnabledSignal || (*mIsEnabledSignal)(this, command->isEnabledParameters()))
- {
- commit(ctrl, param);
- }
-}
-
-LLToolBarButton* LLToolBar::createButton(const LLCommandId& id)
-{
- LLCommand* commandp = LLCommandManager::instance().getCommand(id);
- if (!commandp) return NULL;
-
- LLToolBarButton::Params button_p;
- button_p.name = commandp->name();
- button_p.label = LLTrans::getString(commandp->labelRef());
- button_p.tool_tip = LLTrans::getString(commandp->tooltipRef());
- button_p.image_overlay = LLUI::getUIImage(commandp->icon());
- button_p.button_flash_enable = commandp->isFlashingAllowed();
- button_p.overwriteFrom(mButtonParams[mButtonType]);
- LLToolBarButton* button = LLUICtrlFactory::create<LLToolBarButton>(button_p);
-
- if (!mReadOnly)
- {
- enable_callback_t isEnabledCB;
-
- const std::string& isEnabledFunction = commandp->isEnabledFunctionName();
- if (isEnabledFunction.length() > 0)
- {
- LLUICtrl::EnableCallbackParam isEnabledParam;
- isEnabledParam.function_name = isEnabledFunction;
- isEnabledParam.parameter = commandp->isEnabledParameters();
- isEnabledCB = initEnableCallback(isEnabledParam);
-
- if (NULL == button->mIsEnabledSignal)
- {
- button->mIsEnabledSignal = new enable_signal_t();
- }
-
- button->mIsEnabledSignal->connect(isEnabledCB);
- }
-
- LLUICtrl::CommitCallbackParam executeParam;
- executeParam.function_name = commandp->executeFunctionName();
- executeParam.parameter = commandp->executeParameters();
-
- // If we have a "stop" function then we map the command to mouse down / mouse up otherwise commit
- const std::string& executeStopFunction = commandp->executeStopFunctionName();
- if (executeStopFunction.length() > 0)
- {
- LLUICtrl::CommitCallbackParam executeStopParam;
- executeStopParam.function_name = executeStopFunction;
- executeStopParam.parameter = commandp->executeStopParameters();
- LLUICtrl::commit_callback_t execute_func = initCommitCallback(executeParam);
- button->setFunctionName(commandp->executeFunctionName());
- LL_DEBUGS("UIUsage") << "button function name a -> " << commandp->executeFunctionName() << LL_ENDL;
- LLUICtrl::commit_callback_t stop_func = initCommitCallback(executeStopParam);
-
- button->setMouseDownCallback(boost::bind(&LLToolBarButton::callIfEnabled, button, execute_func, _1, _2));
- button->setMouseUpCallback(boost::bind(&LLToolBarButton::callIfEnabled, button, stop_func, _1, _2));
- }
- else
- {
- button->setFunctionName(commandp->executeFunctionName());
- LL_DEBUGS("UIUsage") << "button function name b -> " << commandp->executeFunctionName() << LL_ENDL;
- button->setCommitCallback(executeParam);
- }
-
- // Set up "is running" query callback
- const std::string& isRunningFunction = commandp->isRunningFunctionName();
- if (isRunningFunction.length() > 0)
- {
- LLUICtrl::EnableCallbackParam isRunningParam;
- isRunningParam.function_name = isRunningFunction;
- isRunningParam.parameter = commandp->isRunningParameters();
- enable_signal_t::slot_type isRunningCB = initEnableCallback(isRunningParam);
-
- if (NULL == button->mIsRunningSignal)
- {
- button->mIsRunningSignal = new enable_signal_t();
- }
-
- button->mIsRunningSignal->connect(isRunningCB);
- }
- }
-
- // Drag and drop behavior must work also if provided in the Toybox and, potentially, any read-only toolbar
- button->setStartDragCallback(mStartDragItemCallback);
- button->setHandleDragCallback(mHandleDragItemCallback);
-
- button->setCommandId(id);
-
- return button;
-}
-
-boost::signals2::connection connectSignal(LLToolBar::button_signal_t*& signal, const LLToolBar::button_signal_t::slot_type& cb)
-{
- if (!signal)
- {
- signal = new LLToolBar::button_signal_t();
- }
-
- return signal->connect(cb);
-}
-
-boost::signals2::connection LLToolBar::setButtonAddCallback(const button_signal_t::slot_type& cb)
-{
- return connectSignal(mButtonAddSignal, cb);
-}
-
-boost::signals2::connection LLToolBar::setButtonEnterCallback(const button_signal_t::slot_type& cb)
-{
- return connectSignal(mButtonEnterSignal, cb);
-}
-
-boost::signals2::connection LLToolBar::setButtonLeaveCallback(const button_signal_t::slot_type& cb)
-{
- return connectSignal(mButtonLeaveSignal, cb);
-}
-
-boost::signals2::connection LLToolBar::setButtonRemoveCallback(const button_signal_t::slot_type& cb)
-{
- return connectSignal(mButtonRemoveSignal, cb);
-}
-
-bool LLToolBar::handleDragAndDrop(S32 x, S32 y, MASK mask, bool drop,
- EDragAndDropType cargo_type,
- void* cargo_data,
- EAcceptance* accept,
- std::string& tooltip_msg)
-{
- // If we have a drop callback, that means that we can handle the drop
- bool handled = mHandleDropCallback != nullptr;
-
- // if drop is set, it's time to call the callback to get the operation done
- if (handled && drop)
- {
- handled = mHandleDropCallback(cargo_data, x, y, this);
- }
-
- // We accept only single tool drop on toolbars
- *accept = handled ? ACCEPT_YES_SINGLE : ACCEPT_NO;
-
- // We'll use that flag to change the visual aspect of the toolbar target on draw()
- mDragAndDropTarget = false;
-
- // Convert drag position into insert position and rank
- if (!isReadOnly() && handled && !drop)
- {
- if (cargo_type == DAD_WIDGET)
- {
- LLInventoryItem* inv_item = (LLInventoryItem*)cargo_data;
- LLCommandId dragged_command(inv_item->getUUID());
- int orig_rank = getRankFromPosition(dragged_command);
- mDragRank = getRankFromPosition(x, y);
- // Don't DaD if we're dragging a command on itself
- mDragAndDropTarget = ((orig_rank != RANK_NONE) && ((mDragRank == orig_rank) || ((mDragRank - 1) == orig_rank)));
- //LL_INFOS() << "Merov debug : DaD, rank = " << mDragRank << ", dragged uui = " << inv_item->getUUID() << LL_ENDL;
- /* Do the following if you want to animate the button itself
- LLCommandId dragged_command(inv_item->getUUID());
- removeCommand(dragged_command);
- addCommand(dragged_command,rank);
- */
- }
- else
- {
- handled = false;
- }
- }
-
- return handled;
-}
-
-LLToolBarButton::LLToolBarButton(const Params& p)
-: LLButton(p),
- mMouseDownX(0),
- mMouseDownY(0),
- mWidthRange(p.button_width),
- mDesiredHeight(p.desired_height),
- mId(""),
- mIsEnabledSignal(NULL),
- mIsRunningSignal(NULL),
- mIsStartingSignal(NULL),
- mIsDragged(false),
- mStartDragItemCallback(NULL),
- mHandleDragItemCallback(NULL),
- mOriginalImageSelected(p.image_selected),
- mOriginalImageUnselected(p.image_unselected),
- mOriginalImagePressed(p.image_pressed),
- mOriginalImagePressedSelected(p.image_pressed_selected),
- mOriginalLabelColor(p.label_color),
- mOriginalLabelColorSelected(p.label_color_selected),
- mOriginalImageOverlayColor(p.image_overlay_color),
- mOriginalImageOverlaySelectedColor(p.image_overlay_selected_color)
-{
-}
-
-LLToolBarButton::~LLToolBarButton()
-{
- delete mIsEnabledSignal;
- delete mIsRunningSignal;
- delete mIsStartingSignal;
-}
-
-bool LLToolBarButton::handleMouseDown(S32 x, S32 y, MASK mask)
-{
- mMouseDownX = x;
- mMouseDownY = y;
- return LLButton::handleMouseDown(x, y, mask);
-}
-
-bool LLToolBarButton::handleHover(S32 x, S32 y, MASK mask)
-{
- bool handled = false;
-
- S32 mouse_distance_squared = (x - mMouseDownX) * (x - mMouseDownX) + (y - mMouseDownY) * (y - mMouseDownY);
- static LLCachedControl<S32> drag_threshold(*LLUI::getInstance()->mSettingGroups["config"], "DragAndDropDistanceThreshold", 3);
- if (mouse_distance_squared > drag_threshold * drag_threshold
- && hasMouseCapture() &&
- mStartDragItemCallback && mHandleDragItemCallback)
- {
- if (!mIsDragged)
- {
- mStartDragItemCallback(x, y, this);
- mIsDragged = true;
- handled = true;
- }
- else
- {
- handled = mHandleDragItemCallback(x, y, mId.uuid(), LLAssetType::AT_WIDGET);
- }
- }
- else
- {
- handled = LLButton::handleHover(x, y, mask);
- }
-
- return handled;
-}
-
-void LLToolBarButton::onMouseEnter(S32 x, S32 y, MASK mask)
-{
- LLUICtrl::onMouseEnter(x, y, mask);
-
- // Always highlight toolbar buttons, even if they are disabled
- if (!gFocusMgr.getMouseCapture() || gFocusMgr.getMouseCapture() == this)
- {
- mNeedsHighlight = true;
- }
-
- LLToolBar* parent_toolbar = getParentByType<LLToolBar>();
- if (parent_toolbar && parent_toolbar->mButtonEnterSignal)
- {
- (*(parent_toolbar->mButtonEnterSignal))(this);
- }
-}
-
-void LLToolBarButton::onMouseLeave(S32 x, S32 y, MASK mask)
-{
- LLButton::onMouseLeave(x, y, mask);
-
- LLToolBar* parent_toolbar = getParentByType<LLToolBar>();
- if (parent_toolbar && parent_toolbar->mButtonLeaveSignal)
- {
- (*(parent_toolbar->mButtonLeaveSignal))(this);
- }
-}
-
-void LLToolBarButton::onMouseCaptureLost()
-{
- mIsDragged = false;
-}
-
-void LLToolBarButton::onCommit()
-{
- LLCommand* command = LLCommandManager::instance().getCommand(mId);
-
- if (!mIsEnabledSignal || (*mIsEnabledSignal)(this, command->isEnabledParameters()))
- {
- LLButton::onCommit();
- }
-}
-
-void LLToolBarButton::reshape(S32 width, S32 height, bool called_from_parent)
-{
- LLButton::reshape(mWidthRange.clamp(width), height, called_from_parent);
-}
-
-void LLToolBarButton::setEnabled(bool enabled)
-{
- if (enabled)
- {
- mImageSelected = mOriginalImageSelected;
- mImageUnselected = mOriginalImageUnselected;
- mImagePressed = mOriginalImagePressed;
- mImagePressedSelected = mOriginalImagePressedSelected;
- mUnselectedLabelColor = mOriginalLabelColor;
- mSelectedLabelColor = mOriginalLabelColorSelected;
- mImageOverlayColor = mOriginalImageOverlayColor;
- mImageOverlaySelectedColor = mOriginalImageOverlaySelectedColor;
- }
- else
- {
- mImageSelected = mImageDisabledSelected;
- mImageUnselected = mImageDisabled;
- mImagePressed = mImageDisabled;
- mImagePressedSelected = mImageDisabledSelected;
- mUnselectedLabelColor = mDisabledLabelColor;
- mSelectedLabelColor = mDisabledSelectedLabelColor;
- mImageOverlayColor = mImageOverlayDisabledColor;
- mImageOverlaySelectedColor = mImageOverlayDisabledColor;
- }
-}
-
-const std::string LLToolBarButton::getToolTip() const
-{
- std::string tooltip;
-
- if (labelIsTruncated() || getCurrentLabel().empty())
- {
- tooltip = LLTrans::getString(LLCommandManager::instance().getCommand(mId)->labelRef()) + " -- " + LLView::getToolTip();
- }
- else
- {
- tooltip = LLView::getToolTip();
- }
-
- LLToolBar* parent_toolbar = getParentByType<LLToolBar>();
- if (parent_toolbar && parent_toolbar->mButtonTooltipSuffix.length() > 0)
- {
- tooltip = tooltip + "\n(" + parent_toolbar->mButtonTooltipSuffix + ")";
- }
-
- return tooltip;
-}
-
-void LLToolBar::LLCenterLayoutPanel::handleReshape(const LLRect& rect, bool by_user)
-{
- LLLayoutPanel::handleReshape(rect, by_user);
-
- if (!mReshapeCallback.empty())
- {
- LLRect r;
- localRectToOtherView(mButtonPanel->getRect(), &r, gFloaterView);
- r.stretch(FLOATER_MIN_VISIBLE_PIXELS);
- mReshapeCallback(mLocationId, r);
- }
-}
+/**
+ * @file lltoolbar.cpp
+ * @author Richard Nelson
+ * @brief User customizable toolbar class
+ *
+ * $LicenseInfo:firstyear=2011&license=viewerlgpl$
+ * Second Life Viewer Source Code
+ * Copyright (C) 2011, Linden Research, Inc.
+ *
+ * This library is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU Lesser General Public
+ * License as published by the Free Software Foundation;
+ * version 2.1 of the License only.
+ *
+ * This library is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+ * Lesser General Public License for more details.
+ *
+ * You should have received a copy of the GNU Lesser General Public
+ * License along with this library; if not, write to the Free Software
+ * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA
+ *
+ * Linden Research, Inc., 945 Battery Street, San Francisco, CA 94111 USA
+ * $/LicenseInfo$
+ */
+
+#include "linden_common.h"
+
+#include "lltoolbar.h"
+
+#include "llcommandmanager.h"
+#include "llmenugl.h"
+#include "lltrans.h"
+#include "llinventory.h"
+#include "lliconctrl.h"
+
+// uncomment this and remove the one in llui.cpp when there is an external reference to this translation unit
+// thanks, MSVC!
+//static LLDefaultChildRegistry::Register<LLToolBar> r1("toolbar");
+
+namespace LLToolBarEnums
+{
+ LLView::EOrientation getOrientation(SideType sideType)
+ {
+ LLView::EOrientation orientation = LLLayoutStack::HORIZONTAL;
+
+ if ((sideType == SIDE_LEFT) || (sideType == SIDE_RIGHT))
+ {
+ orientation = LLLayoutStack::VERTICAL;
+ }
+
+ return orientation;
+ }
+}
+
+using namespace LLToolBarEnums;
+
+
+namespace LLInitParam
+{
+ void TypeValues<ButtonType>::declareValues()
+ {
+ declare("icons_with_text", BTNTYPE_ICONS_WITH_TEXT);
+ declare("icons_only", BTNTYPE_ICONS_ONLY);
+ }
+
+ void TypeValues<SideType>::declareValues()
+ {
+ declare("bottom", SIDE_BOTTOM);
+ declare("left", SIDE_LEFT);
+ declare("right", SIDE_RIGHT);
+ declare("top", SIDE_TOP);
+ }
+}
+
+LLToolBar::Params::Params()
+: button_display_mode("button_display_mode"),
+ commands("command"),
+ side("side", SIDE_TOP),
+ button_icon("button_icon"),
+ button_icon_and_text("button_icon_and_text"),
+ read_only("read_only", false),
+ wrap("wrap", true),
+ pad_left("pad_left"),
+ pad_top("pad_top"),
+ pad_right("pad_right"),
+ pad_bottom("pad_bottom"),
+ pad_between("pad_between"),
+ min_girth("min_girth"),
+ button_panel("button_panel")
+{}
+
+LLToolBar::LLToolBar(const LLToolBar::Params& p)
+: LLUICtrl(p),
+ mReadOnly(p.read_only),
+ mButtonType(p.button_display_mode),
+ mSideType(p.side),
+ mWrap(p.wrap),
+ mNeedsLayout(false),
+ mModified(false),
+ mButtonPanel(NULL),
+ mCenteringStack(NULL),
+ mPadLeft(p.pad_left),
+ mPadRight(p.pad_right),
+ mPadTop(p.pad_top),
+ mPadBottom(p.pad_bottom),
+ mPadBetween(p.pad_between),
+ mMinGirth(p.min_girth),
+ mPopupMenuHandle(),
+ mRightMouseTargetButton(NULL),
+ mStartDragItemCallback(NULL),
+ mHandleDragItemCallback(NULL),
+ mHandleDropCallback(NULL),
+ mButtonAddSignal(NULL),
+ mButtonEnterSignal(NULL),
+ mButtonLeaveSignal(NULL),
+ mButtonRemoveSignal(NULL),
+ mDragAndDropTarget(false),
+ mCaretIcon(NULL),
+ mCenterPanel(NULL)
+{
+ mButtonParams[LLToolBarEnums::BTNTYPE_ICONS_WITH_TEXT] = p.button_icon_and_text;
+ mButtonParams[LLToolBarEnums::BTNTYPE_ICONS_ONLY] = p.button_icon;
+}
+
+LLToolBar::~LLToolBar()
+{
+ auto menu = mPopupMenuHandle.get();
+ if (menu)
+ {
+ menu->die();
+ mPopupMenuHandle.markDead();
+ }
+ delete mButtonAddSignal;
+ delete mButtonEnterSignal;
+ delete mButtonLeaveSignal;
+ delete mButtonRemoveSignal;
+}
+
+void LLToolBar::createContextMenu()
+{
+ if (!mPopupMenuHandle.get())
+ {
+ // Setup bindings specific to this instance for the context menu options
+
+ LLUICtrl::CommitCallbackRegistry::ScopedRegistrar commit_reg;
+ commit_reg.add("Toolbars.EnableSetting", boost::bind(&LLToolBar::onSettingEnable, this, _2));
+ commit_reg.add("Toolbars.RemoveSelectedCommand", boost::bind(&LLToolBar::onRemoveSelectedCommand, this));
+
+ LLUICtrl::EnableCallbackRegistry::ScopedRegistrar enable_reg;
+ enable_reg.add("Toolbars.CheckSetting", boost::bind(&LLToolBar::isSettingChecked, this, _2));
+
+ // Create the context menu
+ llassert(LLMenuGL::sMenuContainer != NULL);
+ LLContextMenu* menu = LLUICtrlFactory::instance().createFromFile<LLContextMenu>("menu_toolbars.xml", LLMenuGL::sMenuContainer, LLMenuHolderGL::child_registry_t::instance());
+
+ if (menu)
+ {
+ menu->setBackgroundColor(LLUIColorTable::instance().getColor("MenuPopupBgColor"));
+ mPopupMenuHandle = menu->getHandle();
+ mRemoveButtonHandle = menu->getChild<LLView>("Remove button")->getHandle();
+ }
+ else
+ {
+ LL_WARNS() << "Unable to load toolbars context menu." << LL_ENDL;
+ }
+ }
+
+ if (mRemoveButtonHandle.get())
+ {
+ // Disable/Enable the "Remove button" menu item depending on whether or not a button was clicked
+ mRemoveButtonHandle.get()->setEnabled(mRightMouseTargetButton != NULL);
+ }
+}
+
+void LLToolBar::initFromParams(const LLToolBar::Params& p)
+{
+ // Initialize the base object
+ LLUICtrl::initFromParams(p);
+
+ LLView::EOrientation orientation = getOrientation(p.side);
+
+ LLLayoutStack::Params centering_stack_p;
+ centering_stack_p.name = "centering_stack";
+ centering_stack_p.rect = getLocalRect();
+ centering_stack_p.follows.flags = FOLLOWS_ALL;
+ centering_stack_p.orientation = orientation;
+ centering_stack_p.mouse_opaque = false;
+
+ mCenteringStack = LLUICtrlFactory::create<LLLayoutStack>(centering_stack_p);
+ addChild(mCenteringStack);
+
+ LLLayoutPanel::Params border_panel_p;
+ border_panel_p.name = "border_panel";
+ border_panel_p.rect = getLocalRect();
+ border_panel_p.auto_resize = true;
+ border_panel_p.user_resize = false;
+ border_panel_p.mouse_opaque = false;
+
+ mCenteringStack->addChild(LLUICtrlFactory::create<LLLayoutPanel>(border_panel_p));
+
+ LLLayoutPanel::Params center_panel_p;
+ center_panel_p.name = "center_panel";
+ center_panel_p.rect = getLocalRect();
+ center_panel_p.auto_resize = false;
+ center_panel_p.user_resize = false;
+ center_panel_p.mouse_opaque = false;
+ mCenterPanel = LLUICtrlFactory::create<LLCenterLayoutPanel>(center_panel_p);
+ mCenteringStack->addChild(mCenterPanel);
+
+ LLPanel::Params button_panel_p(p.button_panel);
+ button_panel_p.rect = mCenterPanel->getLocalRect();
+ button_panel_p.follows.flags = FOLLOWS_BOTTOM|FOLLOWS_LEFT;
+ mButtonPanel = LLUICtrlFactory::create<LLPanel>(button_panel_p);
+ mCenterPanel->setButtonPanel(mButtonPanel);
+ mCenterPanel->addChild(mButtonPanel);
+
+ mCenteringStack->addChild(LLUICtrlFactory::create<LLLayoutPanel>(border_panel_p));
+
+ for (const auto& id : p.commands)
+ {
+ addCommand(id);
+ }
+
+ mNeedsLayout = true;
+}
+
+bool LLToolBar::addCommand(const LLCommandId& commandId, int rank)
+{
+ LLCommand * command = LLCommandManager::instance().getCommand(commandId);
+ if (!command) return false;
+
+ // Create the button and do the things that don't need ordering
+ LLToolBarButton* button = createButton(commandId);
+ mButtonPanel->addChild(button);
+ mButtonMap.insert(std::make_pair(commandId.uuid(), button));
+
+ // Insert the command and button in the right place in their respective lists
+ if ((rank >= mButtonCommands.size()) || (rank == RANK_NONE))
+ {
+ // In that case, back load
+ mButtonCommands.push_back(command->id());
+ mButtons.push_back(button);
+ }
+ else
+ {
+ // Insert in place: iterate to the right spot...
+ std::list<LLToolBarButton*>::iterator it_button = mButtons.begin();
+ command_id_list_t::iterator it_command = mButtonCommands.begin();
+ while (rank > 0)
+ {
+ ++it_button;
+ ++it_command;
+ rank--;
+ }
+ // ...then insert
+ mButtonCommands.insert(it_command, command->id());
+ mButtons.insert(it_button,button);
+ }
+
+ mNeedsLayout = true;
+
+ updateLayoutAsNeeded();
+
+
+ if (mButtonAddSignal)
+ {
+ (*mButtonAddSignal)(button);
+ }
+
+ return true;
+}
+
+// Remove a command from the list
+// Returns the rank of the command in the original list so that doing addCommand(id,rank) right after
+// a removeCommand(id) would leave the list unchanged.
+// Returns RANK_NONE if the command is not found in the list
+int LLToolBar::removeCommand(const LLCommandId& commandId)
+{
+ if (!hasCommand(commandId)) return RANK_NONE;
+
+ // First erase the map record
+ command_id_map::iterator it = mButtonMap.find(commandId.uuid());
+ mButtonMap.erase(it);
+
+ // Now iterate on the commands and buttons to identify the relevant records
+ int rank = 0;
+ std::list<LLToolBarButton*>::iterator it_button = mButtons.begin();
+ command_id_list_t::iterator it_command = mButtonCommands.begin();
+ while (*it_command != commandId)
+ {
+ ++it_button;
+ ++it_command;
+ ++rank;
+ }
+
+ if (mButtonRemoveSignal)
+ {
+ (*mButtonRemoveSignal)(*it_button);
+ }
+
+ // Delete the button and erase the command and button records
+ delete (*it_button);
+ mButtonCommands.erase(it_command);
+ mButtons.erase(it_button);
+
+ mNeedsLayout = true;
+
+ return rank;
+}
+
+void LLToolBar::clearCommandsList()
+{
+ // Clears the commands list
+ mButtonCommands.clear();
+ // This will clear the buttons
+ createButtons();
+}
+
+bool LLToolBar::hasCommand(const LLCommandId& commandId) const
+{
+ if (commandId != LLCommandId::null)
+ {
+ command_id_map::const_iterator it = mButtonMap.find(commandId.uuid());
+ return (it != mButtonMap.end());
+ }
+
+ return false;
+}
+
+bool LLToolBar::enableCommand(const LLCommandId& commandId, bool enabled)
+{
+ LLButton * command_button = NULL;
+
+ if (commandId != LLCommandId::null)
+ {
+ command_id_map::iterator it = mButtonMap.find(commandId.uuid());
+ if (it != mButtonMap.end())
+ {
+ command_button = it->second;
+ command_button->setEnabled(enabled);
+ }
+ }
+
+ return (command_button != NULL);
+}
+
+bool LLToolBar::stopCommandInProgress(const LLCommandId& commandId)
+{
+ //
+ // Note from Leslie:
+ //
+ // This implementation was largely put in place to handle EXP-1348 which is related to
+ // dragging and dropping the "speak" button. The "speak" button can be in one of two
+ // modes, i.e., either a toggle action or a push-to-talk action. Because of this it
+ // responds to mouse down and mouse up in different ways, based on which behavior the
+ // button is currently set to obey. This was the simplest way of getting the button
+ // to turn off the microphone for both behaviors without risking duplicate state.
+ //
+
+ LLToolBarButton * command_button = NULL;
+
+ if (commandId != LLCommandId::null)
+ {
+ LLCommand* command = LLCommandManager::instance().getCommand(commandId);
+ llassert(command);
+
+ // If this command has an explicit function for execution stop
+ if (command->executeStopFunctionName().length() > 0)
+ {
+ command_id_map::iterator it = mButtonMap.find(commandId.uuid());
+ if (it != mButtonMap.end())
+ {
+ command_button = it->second;
+ llassert(command_button->mIsRunningSignal);
+
+ // Check to see if it is running
+ if ((*command_button->mIsRunningSignal)(command_button, command->isRunningParameters()))
+ {
+ // Trigger an additional button commit, which calls mouse down, mouse up and commit
+ command_button->onCommit();
+ }
+ }
+ }
+ }
+
+ return (command_button != NULL);
+}
+
+bool LLToolBar::flashCommand(const LLCommandId& commandId, bool flash, bool force_flashing/* = false */)
+{
+ LLButton * command_button = NULL;
+
+ if (commandId != LLCommandId::null)
+ {
+ command_id_map::iterator it = mButtonMap.find(commandId.uuid());
+ if (it != mButtonMap.end())
+ {
+ command_button = it->second;
+ command_button->setFlashing((bool)(flash),(bool)(force_flashing));
+ }
+ }
+
+ return (command_button != NULL);
+}
+
+bool LLToolBar::handleRightMouseDown(S32 x, S32 y, MASK mask)
+{
+ LLRect button_panel_rect;
+ mButtonPanel->localRectToOtherView(mButtonPanel->getLocalRect(), &button_panel_rect, this);
+ bool handle_it_here = !mReadOnly && button_panel_rect.pointInRect(x, y);
+
+ if (handle_it_here)
+ {
+ // Determine which button the mouse was over during the click in case the context menu action
+ // is intended to affect the button.
+ mRightMouseTargetButton = NULL;
+ for (LLToolBarButton* button : mButtons)
+ {
+ LLRect button_rect;
+ button->localRectToOtherView(button->getLocalRect(), &button_rect, this);
+
+ if (button_rect.pointInRect(x, y))
+ {
+ mRightMouseTargetButton = button;
+ break;
+ }
+ }
+
+ createContextMenu();
+
+ LLContextMenu * menu = (LLContextMenu *) mPopupMenuHandle.get();
+
+ if (menu)
+ {
+ menu->show(x, y);
+
+ LLMenuGL::showPopup(this, menu, x, y);
+ }
+ }
+
+ return handle_it_here;
+}
+
+bool LLToolBar::isSettingChecked(const LLSD& userdata)
+{
+ bool retval = false;
+
+ const std::string setting_name = userdata.asString();
+
+ if (setting_name == "icons_with_text")
+ {
+ retval = (mButtonType == BTNTYPE_ICONS_WITH_TEXT);
+ }
+ else if (setting_name == "icons_only")
+ {
+ retval = (mButtonType == BTNTYPE_ICONS_ONLY);
+ }
+
+ return retval;
+}
+
+void LLToolBar::onSettingEnable(const LLSD& userdata)
+{
+ llassert(!mReadOnly);
+
+ const std::string setting_name = userdata.asString();
+
+ if (setting_name == "icons_with_text")
+ {
+ setButtonType(BTNTYPE_ICONS_WITH_TEXT);
+ }
+ else if (setting_name == "icons_only")
+ {
+ setButtonType(BTNTYPE_ICONS_ONLY);
+ }
+}
+
+void LLToolBar::onRemoveSelectedCommand()
+{
+ llassert(!mReadOnly);
+
+ if (mRightMouseTargetButton)
+ {
+ removeCommand(mRightMouseTargetButton->getCommandId());
+
+ mRightMouseTargetButton = NULL;
+ }
+}
+
+void LLToolBar::setButtonType(LLToolBarEnums::ButtonType button_type)
+{
+ bool regenerate_buttons = (mButtonType != button_type);
+
+ mButtonType = button_type;
+
+ if (regenerate_buttons)
+ {
+ createButtons();
+ }
+}
+
+void LLToolBar::resizeButtonsInRow(std::vector<LLToolBarButton*>& buttons_in_row, S32 max_row_girth)
+{
+ // make buttons in current row all same girth
+ for (LLToolBarButton* button : buttons_in_row)
+ {
+ if (getOrientation(mSideType) == LLLayoutStack::HORIZONTAL)
+ {
+ button->reshape(button->mWidthRange.clamp(button->getRect().getWidth()), max_row_girth);
+ }
+ else // VERTICAL
+ {
+ button->reshape(max_row_girth, button->getRect().getHeight());
+ }
+ }
+}
+
+// Returns the position of the coordinates as a rank in the button list.
+// The rank is the position a tool dropped in (x,y) would assume in the button list.
+// The returned value is between 0 and mButtons.size(), 0 being the first element to the left
+// (or top) and mButtons.size() the last one to the right (or bottom).
+// Various drag data are stored in the toolbar object though are not exposed outside (and shouldn't).
+int LLToolBar::getRankFromPosition(S32 x, S32 y)
+{
+ if (mButtons.empty())
+ {
+ return RANK_NONE;
+ }
+
+ int rank = 0;
+
+ // Convert the toolbar coord into button panel coords
+ LLView::EOrientation orientation = getOrientation(mSideType);
+ S32 button_panel_x = 0;
+ S32 button_panel_y = 0;
+ localPointToOtherView(x, y, &button_panel_x, &button_panel_y, mButtonPanel);
+ S32 dx = x - button_panel_x;
+ S32 dy = y - button_panel_y;
+
+ // Simply compare the passed coord with the buttons outbound box + padding
+ std::list<LLToolBarButton*>::iterator it_button = mButtons.begin();
+ std::list<LLToolBarButton*>::iterator end_button = mButtons.end();
+ LLRect button_rect;
+ while (it_button != end_button)
+ {
+ button_rect = (*it_button)->getRect();
+ S32 point_x = button_rect.mRight + mPadRight;
+ S32 point_y = button_rect.mBottom - mPadBottom;
+
+ if ((button_panel_x < point_x) && (button_panel_y > point_y))
+ {
+ break;
+ }
+ rank++;
+ ++it_button;
+ }
+
+ // Update the passed coordinates to the hit button relevant corner
+ // (different depending on toolbar orientation)
+ if (rank < mButtons.size())
+ {
+ if (orientation == LLLayoutStack::HORIZONTAL)
+ {
+ // Horizontal
+ S32 mid_point = (button_rect.mRight + button_rect.mLeft) / 2;
+ if (button_panel_x < mid_point)
+ {
+ mDragx = button_rect.mLeft - mPadLeft;
+ mDragy = button_rect.mTop + mPadTop;
+ }
+ else
+ {
+ rank++;
+ mDragx = button_rect.mRight + mPadRight - 1;
+ mDragy = button_rect.mTop + mPadTop;
+ }
+ }
+ else
+ {
+ // Vertical
+ S32 mid_point = (button_rect.mTop + button_rect.mBottom) / 2;
+ if (button_panel_y > mid_point)
+ {
+ mDragx = button_rect.mLeft - mPadLeft;
+ mDragy = button_rect.mTop + mPadTop;
+ }
+ else
+ {
+ rank++;
+ mDragx = button_rect.mLeft - mPadLeft;
+ mDragy = button_rect.mBottom - mPadBottom + 1;
+ }
+ }
+ }
+ else
+ {
+ // We hit passed the end of the list so put the insertion point at the end
+ if (orientation == LLLayoutStack::HORIZONTAL)
+ {
+ mDragx = button_rect.mRight + mPadRight;
+ mDragy = button_rect.mTop + mPadTop;
+ }
+ else
+ {
+ mDragx = button_rect.mLeft - mPadLeft;
+ mDragy = button_rect.mBottom - mPadBottom;
+ }
+ }
+
+ // Update the "girth" of the caret, i.e. the width or height (depending of orientation)
+ if (orientation == LLLayoutStack::HORIZONTAL)
+ {
+ mDragGirth = button_rect.getHeight() + mPadBottom + mPadTop;
+ }
+ else
+ {
+ mDragGirth = button_rect.getWidth() + mPadLeft + mPadRight;
+ }
+
+ // The delta account for the coord model change (i.e. convert back to toolbar coord)
+ mDragx += dx;
+ mDragy += dy;
+
+ return rank;
+}
+
+int LLToolBar::getRankFromPosition(const LLCommandId& id)
+{
+ if (!hasCommand(id))
+ {
+ return RANK_NONE;
+ }
+ int rank = 0;
+ std::list<LLToolBarButton*>::iterator it_button = mButtons.begin();
+ std::list<LLToolBarButton*>::iterator end_button = mButtons.end();
+ while (it_button != end_button)
+ {
+ if ((*it_button)->mId == id)
+ {
+ break;
+ }
+ rank++;
+ ++it_button;
+ }
+ return rank;
+}
+
+void LLToolBar::updateLayoutAsNeeded()
+{
+ if (!mNeedsLayout) return;
+
+ LLView::EOrientation orientation = getOrientation(mSideType);
+
+ // our terminology for orientation-agnostic layout is such that
+ // length refers to a distance in the direction we stack the buttons
+ // and girth refers to a distance in the direction buttons wrap
+ S32 max_row_girth = 0;
+ S32 max_row_length = 0;
+
+ S32 max_length;
+ S32 cur_start;
+ S32 cur_row ;
+ S32 row_pad_start;
+ S32 row_pad_end;
+ S32 girth_pad_end;
+ S32 row_running_length;
+
+ if (orientation == LLLayoutStack::HORIZONTAL)
+ {
+ max_length = getRect().getWidth() - mPadLeft - mPadRight;
+ row_pad_start = mPadLeft;
+ row_pad_end = mPadRight;
+ cur_row = mPadTop;
+ girth_pad_end = mPadBottom;
+ }
+ else // VERTICAL
+ {
+ max_length = getRect().getHeight() - mPadTop - mPadBottom;
+ row_pad_start = mPadTop;
+ row_pad_end = mPadBottom;
+ cur_row = mPadLeft;
+ girth_pad_end = mPadRight;
+ }
+
+ row_running_length = row_pad_start;
+ cur_start = row_pad_start;
+
+
+ LLRect panel_rect = mButtonPanel->getLocalRect();
+
+ std::vector<LLToolBarButton*> buttons_in_row;
+
+ for (LLToolBarButton* button : mButtons)
+ {
+ button->reshape(button->mWidthRange.getMin(), button->mDesiredHeight);
+ button->autoResize();
+
+ S32 button_clamped_width = button->mWidthRange.clamp(button->getRect().getWidth());
+ S32 button_length = (orientation == LLLayoutStack::HORIZONTAL)
+ ? button_clamped_width
+ : button->getRect().getHeight();
+ S32 button_girth = (orientation == LLLayoutStack::HORIZONTAL)
+ ? button->getRect().getHeight()
+ : button_clamped_width;
+
+ // wrap if needed
+ if (mWrap
+ && row_running_length + button_length > max_length // out of room...
+ && cur_start != row_pad_start) // ...and not first button in row
+ {
+ if (orientation == LLLayoutStack::VERTICAL)
+ { // row girth (width in this case) is clamped to allowable button widths
+ max_row_girth = button->mWidthRange.clamp(max_row_girth);
+ }
+
+ // make buttons in current row all same girth
+ resizeButtonsInRow(buttons_in_row, max_row_girth);
+ buttons_in_row.clear();
+
+ max_row_length = llmax(max_row_length, row_running_length);
+ row_running_length = row_pad_start;
+ cur_start = row_pad_start;
+ cur_row += max_row_girth + mPadBetween;
+ max_row_girth = 0;
+ }
+
+ LLRect button_rect;
+ if (orientation == LLLayoutStack::HORIZONTAL)
+ {
+ button_rect.setLeftTopAndSize(cur_start, panel_rect.mTop - cur_row, button_clamped_width, button->getRect().getHeight());
+ }
+ else // VERTICAL
+ {
+ button_rect.setLeftTopAndSize(cur_row, panel_rect.mTop - cur_start, button_clamped_width, button->getRect().getHeight());
+ }
+ button->setShape(button_rect);
+
+ buttons_in_row.push_back(button);
+
+ row_running_length += button_length + mPadBetween;
+ cur_start = row_running_length;
+ max_row_girth = llmax(button_girth, max_row_girth);
+ }
+
+ // final resizing in "girth" direction
+ S32 total_girth = cur_row // current row position...
+ + max_row_girth // ...incremented by size of final row...
+ + girth_pad_end; // ...plus padding reserved on end
+ total_girth = llmax(total_girth,mMinGirth);
+
+ max_row_length = llmax(max_row_length, row_running_length - mPadBetween + row_pad_end);
+
+ resizeButtonsInRow(buttons_in_row, max_row_girth);
+
+ // grow and optionally shift toolbar to accommodate buttons
+ if (orientation == LLLayoutStack::HORIZONTAL)
+ {
+ if (mSideType == SIDE_TOP)
+ { // shift down to maintain top edge
+ translate(0, getRect().getHeight() - total_girth);
+ }
+
+ reshape(getRect().getWidth(), total_girth);
+ mButtonPanel->reshape(max_row_length, total_girth);
+ }
+ else // VERTICAL
+ {
+ if (mSideType == SIDE_RIGHT)
+ { // shift left to maintain right edge
+ translate(getRect().getWidth() - total_girth, 0);
+ }
+
+ reshape(total_girth, getRect().getHeight());
+ mButtonPanel->reshape(total_girth, max_row_length);
+ }
+
+ // make parent fit button panel
+ mButtonPanel->getParent()->setShape(mButtonPanel->getLocalRect());
+
+ // re-center toolbar buttons
+ mCenteringStack->updateLayout();
+
+ if (!mButtons.empty())
+ {
+ mButtonPanel->setVisible(true);
+ mButtonPanel->setMouseOpaque(true);
+ }
+
+ // don't clear flag until after we've resized ourselves, to avoid laying out every frame
+ mNeedsLayout = false;
+}
+
+
+void LLToolBar::draw()
+{
+ if (mButtons.empty())
+ {
+ mButtonPanel->setVisible(false);
+ mButtonPanel->setMouseOpaque(false);
+ }
+ else
+ {
+ mButtonPanel->setVisible(true);
+ mButtonPanel->setMouseOpaque(true);
+ }
+
+ // Update enable/disable state and highlight state for editable toolbars
+ if (!mReadOnly)
+ {
+ for (toolbar_button_list::iterator btn_it = mButtons.begin(); btn_it != mButtons.end(); ++btn_it)
+ {
+ LLToolBarButton* btn = *btn_it;
+ LLCommand* command = LLCommandManager::instance().getCommand(btn->mId);
+
+ if (command && btn->mIsEnabledSignal)
+ {
+ const bool button_command_enabled = (*btn->mIsEnabledSignal)(btn, command->isEnabledParameters());
+ btn->setEnabled(button_command_enabled);
+ }
+
+ if (command && btn->mIsRunningSignal)
+ {
+ const bool button_command_running = (*btn->mIsRunningSignal)(btn, command->isRunningParameters());
+ btn->setToggleState(button_command_running);
+ }
+ }
+ }
+
+ updateLayoutAsNeeded();
+ // rect may have shifted during layout
+ LLUI::popMatrix();
+ LLUI::pushMatrix();
+ LLUI::translate((F32)getRect().mLeft, (F32)getRect().mBottom);
+
+ // Position the caret
+ if (!mCaretIcon)
+ {
+ mCaretIcon = getChild<LLIconCtrl>("caret");
+ }
+
+ LLIconCtrl* caret = mCaretIcon;
+ caret->setVisible(false);
+ if (mDragAndDropTarget && !mButtonCommands.empty())
+ {
+ LLRect caret_rect = caret->getRect();
+ if (getOrientation(mSideType) == LLLayoutStack::HORIZONTAL)
+ {
+ caret->setRect(LLRect(mDragx-caret_rect.getWidth()/2+1,
+ mDragy,
+ mDragx+caret_rect.getWidth()/2+1,
+ mDragy-mDragGirth));
+ }
+ else
+ {
+ caret->setRect(LLRect(mDragx,
+ mDragy+caret_rect.getHeight()/2,
+ mDragx+mDragGirth,
+ mDragy-caret_rect.getHeight()/2));
+ }
+ caret->setVisible(true);
+ }
+
+ LLUICtrl::draw();
+ caret->setVisible(false);
+ mDragAndDropTarget = false;
+}
+
+void LLToolBar::reshape(S32 width, S32 height, bool called_from_parent)
+{
+ LLUICtrl::reshape(width, height, called_from_parent);
+ mNeedsLayout = true;
+}
+
+void LLToolBar::createButtons()
+{
+ std::set<LLUUID> set_flashing;
+
+ for (LLToolBarButton* button : mButtons)
+ {
+ if (button->getFlashTimer() && button->getFlashTimer()->isFlashingInProgress())
+ {
+ set_flashing.insert(button->getCommandId().uuid());
+ }
+
+ if (mButtonRemoveSignal)
+ {
+ (*mButtonRemoveSignal)(button);
+ }
+
+ delete button;
+ }
+ mButtons.clear();
+ mButtonMap.clear();
+ mRightMouseTargetButton = NULL;
+
+ for (const LLCommandId& command_id : mButtonCommands)
+ {
+ LLToolBarButton* button = createButton(command_id);
+ mButtons.push_back(button);
+ mButtonPanel->addChild(button);
+ mButtonMap.insert(std::make_pair(command_id.uuid(), button));
+
+ if (mButtonAddSignal)
+ {
+ (*mButtonAddSignal)(button);
+ }
+
+ if (set_flashing.find(button->getCommandId().uuid()) != set_flashing.end())
+ {
+ button->setFlashing(true);
+ }
+ }
+ mNeedsLayout = true;
+}
+
+void LLToolBarButton::callIfEnabled(LLUICtrl::commit_callback_t commit, LLUICtrl* ctrl, const LLSD& param )
+{
+ LLCommand* command = LLCommandManager::instance().getCommand(mId);
+
+ if (!mIsEnabledSignal || (*mIsEnabledSignal)(this, command->isEnabledParameters()))
+ {
+ commit(ctrl, param);
+ }
+}
+
+LLToolBarButton* LLToolBar::createButton(const LLCommandId& id)
+{
+ LLCommand* commandp = LLCommandManager::instance().getCommand(id);
+ if (!commandp) return NULL;
+
+ LLToolBarButton::Params button_p;
+ button_p.name = commandp->name();
+ button_p.label = LLTrans::getString(commandp->labelRef());
+ button_p.tool_tip = LLTrans::getString(commandp->tooltipRef());
+ button_p.image_overlay = LLUI::getUIImage(commandp->icon());
+ button_p.button_flash_enable = commandp->isFlashingAllowed();
+ button_p.overwriteFrom(mButtonParams[mButtonType]);
+ LLToolBarButton* button = LLUICtrlFactory::create<LLToolBarButton>(button_p);
+
+ if (!mReadOnly)
+ {
+ enable_callback_t isEnabledCB;
+
+ const std::string& isEnabledFunction = commandp->isEnabledFunctionName();
+ if (isEnabledFunction.length() > 0)
+ {
+ LLUICtrl::EnableCallbackParam isEnabledParam;
+ isEnabledParam.function_name = isEnabledFunction;
+ isEnabledParam.parameter = commandp->isEnabledParameters();
+ isEnabledCB = initEnableCallback(isEnabledParam);
+
+ if (NULL == button->mIsEnabledSignal)
+ {
+ button->mIsEnabledSignal = new enable_signal_t();
+ }
+
+ button->mIsEnabledSignal->connect(isEnabledCB);
+ }
+
+ LLUICtrl::CommitCallbackParam executeParam;
+ executeParam.function_name = commandp->executeFunctionName();
+ executeParam.parameter = commandp->executeParameters();
+
+ // If we have a "stop" function then we map the command to mouse down / mouse up otherwise commit
+ const std::string& executeStopFunction = commandp->executeStopFunctionName();
+ if (executeStopFunction.length() > 0)
+ {
+ LLUICtrl::CommitCallbackParam executeStopParam;
+ executeStopParam.function_name = executeStopFunction;
+ executeStopParam.parameter = commandp->executeStopParameters();
+ LLUICtrl::commit_callback_t execute_func = initCommitCallback(executeParam);
+ button->setFunctionName(commandp->executeFunctionName());
+ LL_DEBUGS("UIUsage") << "button function name a -> " << commandp->executeFunctionName() << LL_ENDL;
+ LLUICtrl::commit_callback_t stop_func = initCommitCallback(executeStopParam);
+
+ button->setMouseDownCallback(boost::bind(&LLToolBarButton::callIfEnabled, button, execute_func, _1, _2));
+ button->setMouseUpCallback(boost::bind(&LLToolBarButton::callIfEnabled, button, stop_func, _1, _2));
+ }
+ else
+ {
+ button->setFunctionName(commandp->executeFunctionName());
+ LL_DEBUGS("UIUsage") << "button function name b -> " << commandp->executeFunctionName() << LL_ENDL;
+ button->setCommitCallback(executeParam);
+ }
+
+ // Set up "is running" query callback
+ const std::string& isRunningFunction = commandp->isRunningFunctionName();
+ if (isRunningFunction.length() > 0)
+ {
+ LLUICtrl::EnableCallbackParam isRunningParam;
+ isRunningParam.function_name = isRunningFunction;
+ isRunningParam.parameter = commandp->isRunningParameters();
+ enable_signal_t::slot_type isRunningCB = initEnableCallback(isRunningParam);
+
+ if (NULL == button->mIsRunningSignal)
+ {
+ button->mIsRunningSignal = new enable_signal_t();
+ }
+
+ button->mIsRunningSignal->connect(isRunningCB);
+ }
+ }
+
+ // Drag and drop behavior must work also if provided in the Toybox and, potentially, any read-only toolbar
+ button->setStartDragCallback(mStartDragItemCallback);
+ button->setHandleDragCallback(mHandleDragItemCallback);
+
+ button->setCommandId(id);
+
+ return button;
+}
+
+boost::signals2::connection connectSignal(LLToolBar::button_signal_t*& signal, const LLToolBar::button_signal_t::slot_type& cb)
+{
+ if (!signal)
+ {
+ signal = new LLToolBar::button_signal_t();
+ }
+
+ return signal->connect(cb);
+}
+
+boost::signals2::connection LLToolBar::setButtonAddCallback(const button_signal_t::slot_type& cb)
+{
+ return connectSignal(mButtonAddSignal, cb);
+}
+
+boost::signals2::connection LLToolBar::setButtonEnterCallback(const button_signal_t::slot_type& cb)
+{
+ return connectSignal(mButtonEnterSignal, cb);
+}
+
+boost::signals2::connection LLToolBar::setButtonLeaveCallback(const button_signal_t::slot_type& cb)
+{
+ return connectSignal(mButtonLeaveSignal, cb);
+}
+
+boost::signals2::connection LLToolBar::setButtonRemoveCallback(const button_signal_t::slot_type& cb)
+{
+ return connectSignal(mButtonRemoveSignal, cb);
+}
+
+bool LLToolBar::handleDragAndDrop(S32 x, S32 y, MASK mask, bool drop,
+ EDragAndDropType cargo_type,
+ void* cargo_data,
+ EAcceptance* accept,
+ std::string& tooltip_msg)
+{
+ // If we have a drop callback, that means that we can handle the drop
+ bool handled = mHandleDropCallback != nullptr;
+
+ // if drop is set, it's time to call the callback to get the operation done
+ if (handled && drop)
+ {
+ handled = mHandleDropCallback(cargo_data, x, y, this);
+ }
+
+ // We accept only single tool drop on toolbars
+ *accept = handled ? ACCEPT_YES_SINGLE : ACCEPT_NO;
+
+ // We'll use that flag to change the visual aspect of the toolbar target on draw()
+ mDragAndDropTarget = false;
+
+ // Convert drag position into insert position and rank
+ if (!isReadOnly() && handled && !drop)
+ {
+ if (cargo_type == DAD_WIDGET)
+ {
+ LLInventoryItem* inv_item = (LLInventoryItem*)cargo_data;
+ LLCommandId dragged_command(inv_item->getUUID());
+ int orig_rank = getRankFromPosition(dragged_command);
+ mDragRank = getRankFromPosition(x, y);
+ // Don't DaD if we're dragging a command on itself
+ mDragAndDropTarget = ((orig_rank != RANK_NONE) && ((mDragRank == orig_rank) || ((mDragRank - 1) == orig_rank)));
+ //LL_INFOS() << "Merov debug : DaD, rank = " << mDragRank << ", dragged uui = " << inv_item->getUUID() << LL_ENDL;
+ /* Do the following if you want to animate the button itself
+ LLCommandId dragged_command(inv_item->getUUID());
+ removeCommand(dragged_command);
+ addCommand(dragged_command,rank);
+ */
+ }
+ else
+ {
+ handled = false;
+ }
+ }
+
+ return handled;
+}
+
+LLToolBarButton::LLToolBarButton(const Params& p)
+: LLButton(p),
+ mMouseDownX(0),
+ mMouseDownY(0),
+ mWidthRange(p.button_width),
+ mDesiredHeight(p.desired_height),
+ mId(""),
+ mIsEnabledSignal(NULL),
+ mIsRunningSignal(NULL),
+ mIsStartingSignal(NULL),
+ mIsDragged(false),
+ mStartDragItemCallback(NULL),
+ mHandleDragItemCallback(NULL),
+ mOriginalImageSelected(p.image_selected),
+ mOriginalImageUnselected(p.image_unselected),
+ mOriginalImagePressed(p.image_pressed),
+ mOriginalImagePressedSelected(p.image_pressed_selected),
+ mOriginalLabelColor(p.label_color),
+ mOriginalLabelColorSelected(p.label_color_selected),
+ mOriginalImageOverlayColor(p.image_overlay_color),
+ mOriginalImageOverlaySelectedColor(p.image_overlay_selected_color)
+{
+}
+
+LLToolBarButton::~LLToolBarButton()
+{
+ delete mIsEnabledSignal;
+ delete mIsRunningSignal;
+ delete mIsStartingSignal;
+}
+
+bool LLToolBarButton::handleMouseDown(S32 x, S32 y, MASK mask)
+{
+ mMouseDownX = x;
+ mMouseDownY = y;
+ return LLButton::handleMouseDown(x, y, mask);
+}
+
+bool LLToolBarButton::handleHover(S32 x, S32 y, MASK mask)
+{
+ bool handled = false;
+
+ S32 mouse_distance_squared = (x - mMouseDownX) * (x - mMouseDownX) + (y - mMouseDownY) * (y - mMouseDownY);
+ if (mouse_distance_squared > DRAG_N_DROP_DISTANCE_THRESHOLD * DRAG_N_DROP_DISTANCE_THRESHOLD
+ && hasMouseCapture() &&
+ mStartDragItemCallback && mHandleDragItemCallback)
+ {
+ if (!mIsDragged)
+ {
+ mStartDragItemCallback(x, y, this);
+ mIsDragged = true;
+ handled = true;
+ }
+ else
+ {
+ handled = mHandleDragItemCallback(x, y, mId.uuid(), LLAssetType::AT_WIDGET);
+ }
+ }
+ else
+ {
+ handled = LLButton::handleHover(x, y, mask);
+ }
+
+ return handled;
+}
+
+void LLToolBarButton::onMouseEnter(S32 x, S32 y, MASK mask)
+{
+ LLUICtrl::onMouseEnter(x, y, mask);
+
+ // Always highlight toolbar buttons, even if they are disabled
+ if (!gFocusMgr.getMouseCapture() || gFocusMgr.getMouseCapture() == this)
+ {
+ mNeedsHighlight = true;
+ }
+
+ LLToolBar* parent_toolbar = getParentByType<LLToolBar>();
+ if (parent_toolbar && parent_toolbar->mButtonEnterSignal)
+ {
+ (*(parent_toolbar->mButtonEnterSignal))(this);
+ }
+}
+
+void LLToolBarButton::onMouseLeave(S32 x, S32 y, MASK mask)
+{
+ LLButton::onMouseLeave(x, y, mask);
+
+ LLToolBar* parent_toolbar = getParentByType<LLToolBar>();
+ if (parent_toolbar && parent_toolbar->mButtonLeaveSignal)
+ {
+ (*(parent_toolbar->mButtonLeaveSignal))(this);
+ }
+}
+
+void LLToolBarButton::onMouseCaptureLost()
+{
+ mIsDragged = false;
+}
+
+void LLToolBarButton::onCommit()
+{
+ LLCommand* command = LLCommandManager::instance().getCommand(mId);
+
+ if (!mIsEnabledSignal || (*mIsEnabledSignal)(this, command->isEnabledParameters()))
+ {
+ LLButton::onCommit();
+ }
+}
+
+void LLToolBarButton::reshape(S32 width, S32 height, bool called_from_parent)
+{
+ LLButton::reshape(mWidthRange.clamp(width), height, called_from_parent);
+}
+
+void LLToolBarButton::setEnabled(bool enabled)
+{
+ if (enabled)
+ {
+ mImageSelected = mOriginalImageSelected;
+ mImageUnselected = mOriginalImageUnselected;
+ mImagePressed = mOriginalImagePressed;
+ mImagePressedSelected = mOriginalImagePressedSelected;
+ mUnselectedLabelColor = mOriginalLabelColor;
+ mSelectedLabelColor = mOriginalLabelColorSelected;
+ mImageOverlayColor = mOriginalImageOverlayColor;
+ mImageOverlaySelectedColor = mOriginalImageOverlaySelectedColor;
+ }
+ else
+ {
+ mImageSelected = mImageDisabledSelected;
+ mImageUnselected = mImageDisabled;
+ mImagePressed = mImageDisabled;
+ mImagePressedSelected = mImageDisabledSelected;
+ mUnselectedLabelColor = mDisabledLabelColor;
+ mSelectedLabelColor = mDisabledSelectedLabelColor;
+ mImageOverlayColor = mImageOverlayDisabledColor;
+ mImageOverlaySelectedColor = mImageOverlayDisabledColor;
+ }
+}
+
+const std::string LLToolBarButton::getToolTip() const
+{
+ std::string tooltip;
+
+ if (labelIsTruncated() || getCurrentLabel().empty())
+ {
+ tooltip = LLTrans::getString(LLCommandManager::instance().getCommand(mId)->labelRef()) + " -- " + LLView::getToolTip();
+ }
+ else
+ {
+ tooltip = LLView::getToolTip();
+ }
+
+ LLToolBar* parent_toolbar = getParentByType<LLToolBar>();
+ if (parent_toolbar && parent_toolbar->mButtonTooltipSuffix.length() > 0)
+ {
+ tooltip = tooltip + "\n(" + parent_toolbar->mButtonTooltipSuffix + ")";
+ }
+
+ return tooltip;
+}
+
+void LLToolBar::LLCenterLayoutPanel::handleReshape(const LLRect& rect, bool by_user)
+{
+ LLLayoutPanel::handleReshape(rect, by_user);
+
+ if (!mReshapeCallback.empty())
+ {
+ LLRect r;
+ localRectToOtherView(mButtonPanel->getRect(), &r, gFloaterView);
+ r.stretch(FLOATER_MIN_VISIBLE_PIXELS);
+ mReshapeCallback(mLocationId, r);
+ }
+}