/** * @file lltoolbarview.cpp * @author Merov Linden * @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 "llviewerprecompiledheaders.h" #include "lltoolbarview.h" #include "llappviewer.h" #include "llbutton.h" #include "llclipboard.h" #include "lldir.h" #include "lldockablefloater.h" #include "lldockcontrol.h" #include "llimview.h" #include "lltransientfloatermgr.h" #include "lltoolbar.h" #include "lltooldraganddrop.h" #include "llxmlnode.h" #include "llagent.h" // HACK for destinations guide on startup #include "llfloaterreg.h" // HACK for destinations guide on startup #include "llviewercontrol.h" // HACK for destinations guide on startup #include <boost/foreach.hpp> LLToolBarView* gToolBarView = NULL; static LLDefaultChildRegistry::Register<LLToolBarView> r("toolbar_view"); void handleLoginToolbarSetup(); bool isToolDragged() { return (LLToolDragAndDrop::getInstance()->getSource() == LLToolDragAndDrop::SOURCE_VIEWER); } LLToolBarView::Toolbar::Toolbar() : button_display_mode("button_display_mode"), commands("command") {} LLToolBarView::ToolbarSet::ToolbarSet() : left_toolbar("left_toolbar"), right_toolbar("right_toolbar"), bottom_toolbar("bottom_toolbar") {} LLToolBarView::LLToolBarView(const LLToolBarView::Params& p) : LLUICtrl(p), mDragStarted(false), mShowToolbars(true), mDragToolbarButton(NULL), mDragItem(NULL), mToolbarsLoaded(false), mBottomToolbarPanel(NULL) { for (S32 i = 0; i < TOOLBAR_COUNT; i++) { mToolbars[i] = NULL; } } void LLToolBarView::initFromParams(const LLToolBarView::Params& p) { // Initialize the base object LLUICtrl::initFromParams(p); } LLToolBarView::~LLToolBarView() { saveToolbars(); } BOOL LLToolBarView::postBuild() { mToolbars[TOOLBAR_LEFT] = getChild<LLToolBar>("toolbar_left"); mToolbars[TOOLBAR_RIGHT] = getChild<LLToolBar>("toolbar_right"); mToolbars[TOOLBAR_BOTTOM] = getChild<LLToolBar>("toolbar_bottom"); mBottomToolbarPanel = getChild<LLView>("bottom_toolbar_panel"); for (int i = TOOLBAR_FIRST; i <= TOOLBAR_LAST; i++) { mToolbars[i]->setStartDragCallback(boost::bind(LLToolBarView::startDragTool,_1,_2,_3)); mToolbars[i]->setHandleDragCallback(boost::bind(LLToolBarView::handleDragTool,_1,_2,_3,_4)); mToolbars[i]->setHandleDropCallback(boost::bind(LLToolBarView::handleDropTool,_1,_2,_3,_4)); mToolbars[i]->setButtonAddCallback(boost::bind(LLToolBarView::onToolBarButtonAdded,_1)); mToolbars[i]->setButtonRemoveCallback(boost::bind(LLToolBarView::onToolBarButtonRemoved,_1)); } LLAppViewer::instance()->setOnLoginCompletedCallback(boost::bind(&handleLoginToolbarSetup)); return TRUE; } S32 LLToolBarView::hasCommand(const LLCommandId& commandId) const { S32 command_location = TOOLBAR_NONE; for (S32 loc = TOOLBAR_FIRST; loc <= TOOLBAR_LAST; loc++) { if (mToolbars[loc]->hasCommand(commandId)) { command_location = loc; break; } } return command_location; } S32 LLToolBarView::addCommand(const LLCommandId& commandId, EToolBarLocation toolbar, int rank) { int old_rank; removeCommand(commandId, old_rank); S32 command_location = mToolbars[toolbar]->addCommand(commandId, rank); return command_location; } S32 LLToolBarView::removeCommand(const LLCommandId& commandId, int& rank) { S32 command_location = hasCommand(commandId); rank = LLToolBar::RANK_NONE; if (command_location != TOOLBAR_NONE) { rank = mToolbars[command_location]->removeCommand(commandId); } return command_location; } S32 LLToolBarView::enableCommand(const LLCommandId& commandId, bool enabled) { S32 command_location = hasCommand(commandId); if (command_location != TOOLBAR_NONE) { mToolbars[command_location]->enableCommand(commandId, enabled); } return command_location; } S32 LLToolBarView::stopCommandInProgress(const LLCommandId& commandId) { S32 command_location = hasCommand(commandId); if (command_location != TOOLBAR_NONE) { mToolbars[command_location]->stopCommandInProgress(commandId); } return command_location; } S32 LLToolBarView::flashCommand(const LLCommandId& commandId, bool flash) { S32 command_location = hasCommand(commandId); if (command_location != TOOLBAR_NONE) { mToolbars[command_location]->flashCommand(commandId, flash); } return command_location; } bool LLToolBarView::addCommandInternal(const LLCommandId& command, LLToolBar* toolbar) { LLCommandManager& mgr = LLCommandManager::instance(); if (mgr.getCommand(command)) { toolbar->addCommand(command); } else { llwarns << "Toolbars creation : the command with id " << command.uuid().asString() << " cannot be found in the command manager" << llendl; return false; } return true; } bool LLToolBarView::loadToolbars(bool force_default) { LLToolBarView::ToolbarSet toolbar_set; bool err = false; // Load the toolbars.xml file std::string toolbar_file = gDirUtilp->getExpandedFilename(LL_PATH_PER_SL_ACCOUNT, "toolbars.xml"); if (force_default) { toolbar_file = gDirUtilp->getExpandedFilename(LL_PATH_APP_SETTINGS, "toolbars.xml"); } else if (!gDirUtilp->fileExists(toolbar_file)) { llwarns << "User toolbars def not found -> use default" << llendl; toolbar_file = gDirUtilp->getExpandedFilename(LL_PATH_APP_SETTINGS, "toolbars.xml"); } LLXMLNodePtr root; if(!LLXMLNode::parseFile(toolbar_file, root, NULL)) { llwarns << "Unable to load toolbars from file: " << toolbar_file << llendl; err = true; } if (!err && !root->hasName("toolbars")) { llwarns << toolbar_file << " is not a valid toolbars definition file" << llendl; err = true; } // Parse the toolbar settings LLXUIParser parser; if (!err) { parser.readXUI(root, toolbar_set, toolbar_file); } if (!err && !toolbar_set.validateBlock()) { llwarns << "Unable to validate toolbars from file: " << toolbar_file << llendl; err = true; } if (err) { if (force_default) { llerrs << "Unable to load toolbars from default file : " << toolbar_file << llendl; return false; } // Try to load the default toolbars return loadToolbars(true); } // Clear the toolbars now before adding the loaded commands and settings for (S32 i = TOOLBAR_FIRST; i <= TOOLBAR_LAST; i++) { if (mToolbars[i]) { mToolbars[i]->clearCommandsList(); } } // Add commands to each toolbar if (toolbar_set.left_toolbar.isProvided() && mToolbars[TOOLBAR_LEFT]) { if (toolbar_set.left_toolbar.button_display_mode.isProvided()) { LLToolBarEnums::ButtonType button_type = toolbar_set.left_toolbar.button_display_mode; mToolbars[TOOLBAR_LEFT]->setButtonType(button_type); } BOOST_FOREACH(const LLCommandId::Params& command_params, toolbar_set.left_toolbar.commands) { if (addCommandInternal(LLCommandId(command_params), mToolbars[TOOLBAR_LEFT])) { llwarns << "Error adding command '" << command_params.name() << "' to left toolbar." << llendl; } } } if (toolbar_set.right_toolbar.isProvided() && mToolbars[TOOLBAR_RIGHT]) { if (toolbar_set.right_toolbar.button_display_mode.isProvided()) { LLToolBarEnums::ButtonType button_type = toolbar_set.right_toolbar.button_display_mode; mToolbars[TOOLBAR_RIGHT]->setButtonType(button_type); } BOOST_FOREACH(const LLCommandId::Params& command_params, toolbar_set.right_toolbar.commands) { if (addCommandInternal(LLCommandId(command_params), mToolbars[TOOLBAR_RIGHT])) { llwarns << "Error adding command '" << command_params.name() << "' to right toolbar." << llendl; } } } if (toolbar_set.bottom_toolbar.isProvided() && mToolbars[TOOLBAR_BOTTOM]) { if (toolbar_set.bottom_toolbar.button_display_mode.isProvided()) { LLToolBarEnums::ButtonType button_type = toolbar_set.bottom_toolbar.button_display_mode; mToolbars[TOOLBAR_BOTTOM]->setButtonType(button_type); } BOOST_FOREACH(const LLCommandId::Params& command_params, toolbar_set.bottom_toolbar.commands) { if (addCommandInternal(LLCommandId(command_params), mToolbars[TOOLBAR_BOTTOM])) { llwarns << "Error adding command '" << command_params.name() << "' to bottom toolbar." << llendl; } } } mToolbarsLoaded = true; return true; } bool LLToolBarView::clearToolbars() { for (S32 i = TOOLBAR_FIRST; i <= TOOLBAR_LAST; i++) { if (mToolbars[i]) { mToolbars[i]->clearCommandsList(); } } return true; } //static bool LLToolBarView::loadDefaultToolbars() { bool retval = false; if (gToolBarView) { retval = gToolBarView->loadToolbars(true); if (retval) { gToolBarView->saveToolbars(); } } return retval; } //static bool LLToolBarView::clearAllToolbars() { bool retval = false; if (gToolBarView) { retval = gToolBarView->clearToolbars(); if (retval) { gToolBarView->saveToolbars(); } } return retval; } void LLToolBarView::saveToolbars() const { if (!mToolbarsLoaded) return; // Build the parameter tree from the toolbar data LLToolBarView::ToolbarSet toolbar_set; if (mToolbars[TOOLBAR_LEFT]) { toolbar_set.left_toolbar.button_display_mode = mToolbars[TOOLBAR_LEFT]->getButtonType(); addToToolset(mToolbars[TOOLBAR_LEFT]->getCommandsList(), toolbar_set.left_toolbar); } if (mToolbars[TOOLBAR_RIGHT]) { toolbar_set.right_toolbar.button_display_mode = mToolbars[TOOLBAR_RIGHT]->getButtonType(); addToToolset(mToolbars[TOOLBAR_RIGHT]->getCommandsList(), toolbar_set.right_toolbar); } if (mToolbars[TOOLBAR_BOTTOM]) { toolbar_set.bottom_toolbar.button_display_mode = mToolbars[TOOLBAR_BOTTOM]->getButtonType(); addToToolset(mToolbars[TOOLBAR_BOTTOM]->getCommandsList(), toolbar_set.bottom_toolbar); } // Serialize the parameter tree LLXMLNodePtr output_node = new LLXMLNode("toolbars", false); LLXUIParser parser; parser.writeXUI(output_node, toolbar_set); // Write the resulting XML to file if(!output_node->isNull()) { const std::string& filename = gDirUtilp->getExpandedFilename(LL_PATH_PER_SL_ACCOUNT, "toolbars.xml"); LLFILE *fp = LLFile::fopen(filename, "w"); if (fp != NULL) { LLXMLNode::writeHeaderToFile(fp); output_node->writeToFile(fp); fclose(fp); } } } // Enumerate the commands in command_list and add them as Params to the toolbar void LLToolBarView::addToToolset(command_id_list_t& command_list, Toolbar& toolbar) const { LLCommandManager& mgr = LLCommandManager::instance(); for (command_id_list_t::const_iterator it = command_list.begin(); it != command_list.end(); ++it) { LLCommand* command = mgr.getCommand(*it); if (command) { LLCommandId::Params command_name_param; command_name_param.name = command->name(); toolbar.commands.add(command_name_param); } } } void LLToolBarView::onToolBarButtonAdded(LLView* button) { llassert(button); if (button->getName() == "speak") { // Add the "Speak" button as a control view in LLTransientFloaterMgr // to prevent hiding the transient IM floater upon pressing "Speak". LLTransientFloaterMgr::getInstance()->addControlView(button); // Redock incoming and/or outgoing call windows, if applicable LLFloater* incoming_floater = LLFloaterReg::getLastFloaterInGroup("incoming_call"); LLFloater* outgoing_floater = LLFloaterReg::getLastFloaterInGroup("outgoing_call"); if (incoming_floater && incoming_floater->isShown()) { LLCallDialog* incoming = dynamic_cast<LLCallDialog *>(incoming_floater); llassert(incoming); LLDockControl* dock_control = incoming->getDockControl(); if (dock_control->getDock() == NULL) { incoming->dockToToolbarButton("speak"); } } if (outgoing_floater && outgoing_floater->isShown()) { LLCallDialog* outgoing = dynamic_cast<LLCallDialog *>(outgoing_floater); llassert(outgoing); LLDockControl* dock_control = outgoing->getDockControl(); if (dock_control->getDock() == NULL) { outgoing->dockToToolbarButton("speak"); } } } else if (button->getName() == "voice") { // Add the "Voice controls" button as a control view in LLTransientFloaterMgr // to prevent hiding the transient IM floater upon pressing "Voice controls". LLTransientFloaterMgr::getInstance()->addControlView(button); } } void LLToolBarView::onToolBarButtonRemoved(LLView* button) { llassert(button); if (button->getName() == "speak") { LLTransientFloaterMgr::getInstance()->removeControlView(button); // Undock incoming and/or outgoing call windows LLFloater* incoming_floater = LLFloaterReg::getLastFloaterInGroup("incoming_call"); LLFloater* outgoing_floater = LLFloaterReg::getLastFloaterInGroup("outgoing_call"); if (incoming_floater && incoming_floater->isShown()) { LLDockableFloater* incoming = dynamic_cast<LLDockableFloater *>(incoming_floater); llassert(incoming); LLDockControl* dock_control = incoming->getDockControl(); dock_control->setDock(NULL); } if (outgoing_floater && outgoing_floater->isShown()) { LLDockableFloater* outgoing = dynamic_cast<LLDockableFloater *>(outgoing_floater); llassert(outgoing); LLDockControl* dock_control = outgoing->getDockControl(); dock_control->setDock(NULL); } } else if (button->getName() == "voice") { LLTransientFloaterMgr::getInstance()->removeControlView(button); } } void LLToolBarView::draw() { LLRect toolbar_rects[TOOLBAR_COUNT]; for (S32 i = TOOLBAR_FIRST; i <= TOOLBAR_LAST; i++) { if (mToolbars[i]) { LLLayoutStack::ELayoutOrientation orientation = LLToolBarEnums::getOrientation(mToolbars[i]->getSideType()); if (orientation == LLLayoutStack::HORIZONTAL) { mToolbars[i]->getParent()->reshape(mToolbars[i]->getParent()->getRect().getWidth(), mToolbars[i]->getRect().getHeight()); } else { mToolbars[i]->getParent()->reshape(mToolbars[i]->getRect().getWidth(), mToolbars[i]->getParent()->getRect().getHeight()); } mToolbars[i]->localRectToOtherView(mToolbars[i]->getLocalRect(), &toolbar_rects[i], this); } } for (S32 i = TOOLBAR_FIRST; i <= TOOLBAR_LAST; i++) { mToolbars[i]->getParent()->setVisible(mShowToolbars && (mToolbars[i]->hasButtons() || isToolDragged())); } // Draw drop zones if drop of a tool is active if (isToolDragged()) { LLColor4 drop_color = LLUIColorTable::instance().getColor( "ToolbarDropZoneColor" ); for (S32 i = TOOLBAR_FIRST; i <= TOOLBAR_LAST; i++) { gl_rect_2d(toolbar_rects[i], drop_color, TRUE); } } LLUICtrl::draw(); } // ---------------------------------------- // Drag and Drop Handling // ---------------------------------------- void LLToolBarView::startDragTool(S32 x, S32 y, LLToolBarButton* toolbarButton) { resetDragTool(toolbarButton); // Flag the tool dragging but don't start it yet LLToolDragAndDrop::getInstance()->setDragStart( x, y ); } BOOL LLToolBarView::handleDragTool( S32 x, S32 y, const LLUUID& uuid, LLAssetType::EType type) { if (LLToolDragAndDrop::getInstance()->isOverThreshold( x, y )) { if (!gToolBarView->mDragStarted) { // Start the tool dragging: // First, create the global drag and drop object std::vector<EDragAndDropType> types; uuid_vec_t cargo_ids; types.push_back(DAD_WIDGET); cargo_ids.push_back(uuid); LLToolDragAndDrop::ESource src = LLToolDragAndDrop::SOURCE_VIEWER; LLUUID srcID; LLToolDragAndDrop::getInstance()->beginMultiDrag(types, cargo_ids, src, srcID); // Second, stop the command if it is in progress and requires stopping! LLCommandId command_id = LLCommandId(uuid); gToolBarView->stopCommandInProgress(command_id); gToolBarView->mDragStarted = true; return TRUE; } else { MASK mask = 0; return LLToolDragAndDrop::getInstance()->handleHover( x, y, mask ); } } return FALSE; } BOOL LLToolBarView::handleDropTool( void* cargo_data, S32 x, S32 y, LLToolBar* toolbar) { BOOL handled = FALSE; LLInventoryObject* inv_item = static_cast<LLInventoryObject*>(cargo_data); LLAssetType::EType type = inv_item->getType(); if (type == LLAssetType::AT_WIDGET) { handled = TRUE; // Get the command from its uuid LLCommandManager& mgr = LLCommandManager::instance(); LLCommandId command_id(inv_item->getUUID()); LLCommand* command = mgr.getCommand(command_id); if (command) { // Suppress the command from the toolbars (including the one it's dropped in, // this will handle move position). S32 old_toolbar_loc = gToolBarView->hasCommand(command_id); LLToolBar* old_toolbar = NULL; if (old_toolbar_loc != TOOLBAR_NONE) { llassert(gToolBarView->mDragToolbarButton); old_toolbar = gToolBarView->mDragToolbarButton->getParentByType<LLToolBar>(); if (old_toolbar->isReadOnly() && toolbar->isReadOnly()) { // do nothing } else { int old_rank = LLToolBar::RANK_NONE; gToolBarView->removeCommand(command_id, old_rank); } } // Convert the (x,y) position in rank in toolbar if (!toolbar->isReadOnly()) { int new_rank = toolbar->getRankFromPosition(x,y); toolbar->addCommand(command_id, new_rank); } // Save the new toolbars configuration gToolBarView->saveToolbars(); } else { llwarns << "Command couldn't be found in command manager" << llendl; } } resetDragTool(NULL); return handled; } void LLToolBarView::resetDragTool(LLToolBarButton* toolbarButton) { // Clear the saved command, toolbar and rank gToolBarView->mDragStarted = false; gToolBarView->mDragToolbarButton = toolbarButton; } // Provide a handle on a free standing inventory item containing references to the tool. // This might be used by Drag and Drop to move around references to tool items. LLInventoryObject* LLToolBarView::getDragItem() { if (mDragToolbarButton) { LLUUID item_uuid = mDragToolbarButton->getCommandId().uuid(); mDragItem = new LLInventoryObject (item_uuid, LLUUID::null, LLAssetType::AT_WIDGET, ""); } return mDragItem; } void LLToolBarView::setToolBarsVisible(bool visible) { mShowToolbars = visible; } bool LLToolBarView::isModified() const { bool modified = false; for (S32 i = TOOLBAR_FIRST; i <= TOOLBAR_LAST; i++) { modified |= mToolbars[i]->isModified(); } return modified; } // // HACK to bring up destinations guide at startup // void handleLoginToolbarSetup() { // Open the destinations guide by default on first login, per Rhett if (gSavedPerAccountSettings.getBOOL("DisplayDestinationsOnInitialRun") || gAgent.isFirstLogin()) { LLFloaterReg::showInstance("destinations"); gSavedPerAccountSettings.setBOOL("DisplayDestinationsOnInitialRun", FALSE); } }