summaryrefslogtreecommitdiff
path: root/indra/newview/llpanelplaces.cpp
diff options
context:
space:
mode:
authorAnsariel <ansariel.hiller@phoenixviewer.com>2024-05-22 21:25:21 +0200
committerAndrey Lihatskiy <alihatskiy@productengine.com>2024-05-22 22:40:26 +0300
commite2e37cced861b98de8c1a7c9c0d3a50d2d90e433 (patch)
tree1bb897489ce524986f6196201c10ac0d8861aa5f /indra/newview/llpanelplaces.cpp
parent069ea06848f766466f1a281144c82a0f2bd79f3a (diff)
Fix line endlings
Diffstat (limited to 'indra/newview/llpanelplaces.cpp')
-rw-r--r--indra/newview/llpanelplaces.cpp2716
1 files changed, 1358 insertions, 1358 deletions
diff --git a/indra/newview/llpanelplaces.cpp b/indra/newview/llpanelplaces.cpp
index 6baa223389..7deb1d9fd4 100644
--- a/indra/newview/llpanelplaces.cpp
+++ b/indra/newview/llpanelplaces.cpp
@@ -1,1358 +1,1358 @@
-/**
- * @file llpanelplaces.cpp
- * @brief Side Bar "Places" panel
- *
- * $LicenseInfo:firstyear=2009&license=viewerlgpl$
- * Second Life Viewer Source Code
- * Copyright (C) 2010, 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 "llpanelplaces.h"
-
-#include "llassettype.h"
-#include "lltimer.h"
-
-#include "llinventory.h"
-#include "lllandmark.h"
-#include "llparcel.h"
-
-#include "llcombobox.h"
-#include "llfiltereditor.h"
-#include "llfirstuse.h"
-#include "llfloaterreg.h"
-#include "llfloatersidepanelcontainer.h"
-#include "llmenubutton.h"
-#include "llnotificationsutil.h"
-#include "lltabcontainer.h"
-#include "lltexteditor.h"
-#include "lltrans.h"
-#include "lluictrlfactory.h"
-
-#include "llwindow.h"
-
-#include "llagent.h"
-#include "llagentpicksinfo.h"
-#include "llavatarpropertiesprocessor.h"
-#include "llcommandhandler.h"
-#include "lldndbutton.h"
-#include "llfloaterworldmap.h"
-#include "llinventorybridge.h"
-#include "llinventoryobserver.h"
-#include "llinventorymodel.h"
-#include "lllandmarkactions.h"
-#include "lllandmarklist.h"
-#include "lllayoutstack.h"
-#include "llpanellandmarkinfo.h"
-#include "llpanellandmarks.h"
-#include "llpanelplaceprofile.h"
-#include "llpanelteleporthistory.h"
-#include "llremoteparcelrequest.h"
-#include "llteleporthistorystorage.h"
-#include "lltoggleablemenu.h"
-#include "llviewerinventory.h"
-#include "llviewermenu.h"
-#include "llviewermessage.h"
-#include "llviewerparcelmgr.h"
-#include "llviewerregion.h"
-#include "llviewerwindow.h"
-
-// Constants
-static const F32 PLACE_INFO_UPDATE_INTERVAL = 3.0;
-static const std::string AGENT_INFO_TYPE = "agent";
-static const std::string CREATE_LANDMARK_INFO_TYPE = "create_landmark";
-static const std::string CREATE_PICK_TYPE = "create_pick";
-static const std::string LANDMARK_INFO_TYPE = "landmark";
-static const std::string REMOTE_PLACE_INFO_TYPE = "remote_place";
-static const std::string TELEPORT_HISTORY_INFO_TYPE = "teleport_history";
-static const std::string LANDMARK_TAB_INFO_TYPE = "open_landmark_tab";
-
-// Support for secondlife:///app/parcel/{UUID}/about SLapps
-class LLParcelHandler : public LLCommandHandler
-{
-public:
- // requires trusted browser to trigger
- LLParcelHandler() : LLCommandHandler("parcel", UNTRUSTED_THROTTLE) { }
- bool handle(const LLSD& params,
- const LLSD& query_map,
- const std::string& grid,
- LLMediaCtrl* web)
- {
- if (params.size() < 2)
- {
- return false;
- }
-
- if (!LLUI::getInstance()->mSettingGroups["config"]->getBOOL("EnablePlaceProfile"))
- {
- LLNotificationsUtil::add("NoPlaceInfo", LLSD(), LLSD(), std::string("SwitchToStandardSkinAndQuit"));
- return true;
- }
-
- LLUUID parcel_id;
- if (!parcel_id.set(params[0], false))
- {
- return false;
- }
- if (params[1].asString() == "about")
- {
- if (parcel_id.notNull())
- {
- LLSD key;
- key["type"] = "remote_place";
- key["id"] = parcel_id;
- LLFloaterSidePanelContainer::showPanel("places", key);
- return true;
- }
- }
- return false;
- }
-};
-LLParcelHandler gParcelHandler;
-
-// Helper functions
-static bool is_agent_in_selected_parcel(LLParcel* parcel);
-static void onSLURLBuilt(std::string& slurl);
-
-//Observer classes
-class LLPlacesParcelObserver : public LLParcelObserver
-{
-public:
- LLPlacesParcelObserver(LLPanelPlaces* places_panel) :
- LLParcelObserver(),
- mPlaces(places_panel)
- {}
-
- /*virtual*/ void changed()
- {
- if (mPlaces)
- mPlaces->changedParcelSelection();
- }
-
-private:
- LLPanelPlaces* mPlaces;
-};
-
-class LLPlacesInventoryObserver : public LLInventoryAddedObserver
-{
-public:
- LLPlacesInventoryObserver(LLPanelPlaces* places_panel) :
- mPlaces(places_panel)
- {}
-
- /*virtual*/ void changed(U32 mask)
- {
- LLInventoryAddedObserver::changed(mask);
-
- if (mPlaces && !mPlaces->tabsCreated())
- {
- mPlaces->createTabs();
- }
- }
-
-protected:
- /*virtual*/ void done()
- {
- mPlaces->showAddedLandmarkInfo(gInventory.getAddedIDs());
- }
-
-private:
- LLPanelPlaces* mPlaces;
-};
-
-class LLPlacesRemoteParcelInfoObserver : public LLRemoteParcelInfoObserver
-{
-public:
- LLPlacesRemoteParcelInfoObserver(LLPanelPlaces* places_panel) :
- LLRemoteParcelInfoObserver(),
- mPlaces(places_panel)
- {}
-
- ~LLPlacesRemoteParcelInfoObserver()
- {
- // remove any in-flight observers
- std::set<LLUUID>::iterator it;
- for (it = mParcelIDs.begin(); it != mParcelIDs.end(); ++it)
- {
- const LLUUID &id = *it;
- LLRemoteParcelInfoProcessor::getInstance()->removeObserver(id, this);
- }
- mParcelIDs.clear();
- }
-
- /*virtual*/ void processParcelInfo(const LLParcelData& parcel_data)
- {
- if (mPlaces)
- {
- mPlaces->changedGlobalPos(LLVector3d(parcel_data.global_x,
- parcel_data.global_y,
- parcel_data.global_z));
- }
-
- mParcelIDs.erase(parcel_data.parcel_id);
- LLRemoteParcelInfoProcessor::getInstance()->removeObserver(parcel_data.parcel_id, this);
- }
- /*virtual*/ void setParcelID(const LLUUID& parcel_id)
- {
- if (!parcel_id.isNull())
- {
- mParcelIDs.insert(parcel_id);
- LLRemoteParcelInfoProcessor::getInstance()->addObserver(parcel_id, this);
- LLRemoteParcelInfoProcessor::getInstance()->sendParcelInfoRequest(parcel_id);
- }
- }
- /*virtual*/ void setErrorStatus(S32 status, const std::string& reason)
- {
- LL_ERRS() << "Can't complete remote parcel request. Http Status: "
- << status << ". Reason : " << reason << LL_ENDL;
- }
-
-private:
- std::set<LLUUID> mParcelIDs;
- LLPanelPlaces* mPlaces;
-};
-
-
-static LLPanelInjector<LLPanelPlaces> t_places("panel_places");
-
-LLPanelPlaces::LLPanelPlaces()
- : LLPanel(),
- mActivePanel(NULL),
- mFilterEditor(NULL),
- mPlaceProfile(NULL),
- mLandmarkInfo(NULL),
- mItem(NULL),
- mPlaceMenu(NULL),
- mLandmarkMenu(NULL),
- mPosGlobal(),
- isLandmarkEditModeOn(false),
- mTabsCreated(false)
-{
- mParcelObserver = new LLPlacesParcelObserver(this);
- mInventoryObserver = new LLPlacesInventoryObserver(this);
- mRemoteParcelObserver = new LLPlacesRemoteParcelInfoObserver(this);
-
- gInventory.addObserver(mInventoryObserver);
-
- mAgentParcelChangedConnection = gAgent.addParcelChangedCallback(
- boost::bind(&LLPanelPlaces::updateVerbs, this));
-
- //buildFromFile( "panel_places.xml"); // Called from LLRegisterPanelClass::defaultPanelClassBuilder()
-}
-
-LLPanelPlaces::~LLPanelPlaces()
-{
- if (gInventory.containsObserver(mInventoryObserver))
- gInventory.removeObserver(mInventoryObserver);
-
- LLViewerParcelMgr::getInstance()->removeObserver(mParcelObserver);
-
- delete mInventoryObserver;
- delete mParcelObserver;
- delete mRemoteParcelObserver;
-
- if (mAgentParcelChangedConnection.connected())
- {
- mAgentParcelChangedConnection.disconnect();
- }
-}
-
-bool LLPanelPlaces::postBuild()
-{
- mTeleportBtn = getChild<LLButton>("teleport_btn");
- mTeleportBtn->setClickedCallback(boost::bind(&LLPanelPlaces::onTeleportButtonClicked, this));
-
- mShowOnMapBtn = getChild<LLButton>("map_btn");
- mShowOnMapBtn->setClickedCallback(boost::bind(&LLPanelPlaces::onShowOnMapButtonClicked, this));
-
- mSaveBtn = getChild<LLButton>("save_btn");
- mSaveBtn->setClickedCallback(boost::bind(&LLPanelPlaces::onSaveButtonClicked, this));
-
- mCancelBtn = getChild<LLButton>("cancel_btn");
- mCancelBtn->setClickedCallback(boost::bind(&LLPanelPlaces::onCancelButtonClicked, this));
-
- mCloseBtn = getChild<LLButton>("close_btn");
- mCloseBtn->setClickedCallback(boost::bind(&LLPanelPlaces::onBackButtonClicked, this));
-
- mOverflowBtn = getChild<LLMenuButton>("overflow_btn");
- mOverflowBtn->setMouseDownCallback(boost::bind(&LLPanelPlaces::onOverflowButtonClicked, this));
-
- mGearMenuButton = getChild<LLMenuButton>("options_gear_btn");
- mGearMenuButton->setMouseDownCallback(boost::bind(&LLPanelPlaces::onGearMenuClick, this));
-
- mSortingMenuButton = getChild<LLMenuButton>("sorting_menu_btn");
- mSortingMenuButton->setMouseDownCallback(boost::bind(&LLPanelPlaces::onSortingMenuClick, this));
-
- mAddMenuButton = getChild<LLMenuButton>("add_menu_btn");
- mAddMenuButton->setMouseDownCallback(boost::bind(&LLPanelPlaces::onAddMenuClick, this));
-
- mRemoveSelectedBtn = getChild<LLButton>("trash_btn");
- mRemoveSelectedBtn->setClickedCallback(boost::bind(&LLPanelPlaces::onRemoveButtonClicked, this));
-
- LLDragAndDropButton* trash_btn = (LLDragAndDropButton*)mRemoveSelectedBtn;
- trash_btn->setDragAndDropHandler(boost::bind(&LLPanelPlaces::handleDragAndDropToTrash, this
- , _4 // bool drop
- , _5 // EDragAndDropType cargo_type
- , _6 // void* cargo_data
- , _7 // EAcceptance* accept
- ));
-
- LLUICtrl::CommitCallbackRegistry::ScopedRegistrar registrar;
- registrar.add("Places.OverflowMenu.Action", boost::bind(&LLPanelPlaces::onOverflowMenuItemClicked, this, _2));
- LLUICtrl::EnableCallbackRegistry::ScopedRegistrar enable_registrar;
- enable_registrar.add("Places.OverflowMenu.Enable", boost::bind(&LLPanelPlaces::onOverflowMenuItemEnable, this, _2));
-
- mPlaceMenu = LLUICtrlFactory::getInstance()->createFromFile<LLToggleableMenu>("menu_place.xml", gMenuHolder, LLViewerMenuHolderGL::child_registry_t::instance());
- if (mPlaceMenu)
- {
- mPlaceMenu->setAlwaysShowMenu(true);
- }
- else
- {
- LL_WARNS() << "Error loading Place menu" << LL_ENDL;
- }
-
- mLandmarkMenu = LLUICtrlFactory::getInstance()->createFromFile<LLToggleableMenu>("menu_landmark.xml", gMenuHolder, LLViewerMenuHolderGL::child_registry_t::instance());
- if (!mLandmarkMenu)
- {
- LL_WARNS() << "Error loading Landmark menu" << LL_ENDL;
- }
-
- mTabContainer = getChild<LLTabContainer>("Places Tabs");
- if (mTabContainer)
- {
- mTabContainer->setCommitCallback(boost::bind(&LLPanelPlaces::onTabSelected, this));
- }
-
- mButtonsContainer = getChild<LLPanel>("button_layout_panel");
- mButtonsContainer->setVisible(false);
- mFilterContainer = getChild<LLLayoutStack>("top_menu_panel");
-
- mFilterEditor = getChild<LLFilterEditor>("Filter");
- if (mFilterEditor)
- {
- //when list item is being clicked the filter editor looses focus
- //committing on focus lost leads to detaching list items
- //BUT a detached list item cannot be made selected and must not be clicked onto
- mFilterEditor->setCommitOnFocusLost(false);
-
- mFilterEditor->setCommitCallback(boost::bind(&LLPanelPlaces::onFilterEdit, this, _2, false));
- }
-
- mPlaceProfile = findChild<LLPanelPlaceProfile>("panel_place_profile");
- mLandmarkInfo = findChild<LLPanelLandmarkInfo>("panel_landmark_info");
- if (!mPlaceProfile || !mLandmarkInfo)
- return false;
-
- mPlaceProfileBackBtn = mPlaceProfile->getChild<LLButton>("back_btn");
- mPlaceProfileBackBtn->setClickedCallback(boost::bind(&LLPanelPlaces::onBackButtonClicked, this));
-
- mLandmarkInfo->getChild<LLButton>("back_btn")->setClickedCallback(boost::bind(&LLPanelPlaces::onBackButtonClicked, this));
-
- LLLineEditor* title_editor = mLandmarkInfo->getChild<LLLineEditor>("title_editor");
- title_editor->setKeystrokeCallback(boost::bind(&LLPanelPlaces::onEditButtonClicked, this), NULL);
-
- LLTextEditor* notes_editor = mLandmarkInfo->getChild<LLTextEditor>("notes_editor");
- notes_editor->setKeystrokeCallback(boost::bind(&LLPanelPlaces::onEditButtonClicked, this));
-
- LLComboBox* folder_combo = mLandmarkInfo->getChild<LLComboBox>("folder_combo");
- folder_combo->setCommitCallback(boost::bind(&LLPanelPlaces::onEditButtonClicked, this));
-
- LLButton* edit_btn = mLandmarkInfo->getChild<LLButton>("edit_btn");
- edit_btn->setCommitCallback(boost::bind(&LLPanelPlaces::onEditButtonClicked, this));
-
- createTabs();
- updateVerbs();
-
- return true;
-}
-
-void LLPanelPlaces::onOpen(const LLSD& key)
-{
- if (!mPlaceProfile || !mLandmarkInfo)
- return;
-
- if (key.size() != 0)
- {
- isLandmarkEditModeOn = false;
- std::string key_type = key["type"].asString();
- if (key_type == LANDMARK_TAB_INFO_TYPE)
- {
- // Small hack: We need to toggle twice. The first toggle moves from the Landmark
- // or Teleport History info panel to the Landmark or Teleport History list panel.
- // For this first toggle, the mPlaceInfoType should be the one previously used so
- // that the state can be corretly set.
- // The second toggle forces the list to be set to Landmark.
- // This avoids extracting and duplicating all the state logic from togglePlaceInfoPanel()
- // here or some specific private method
- togglePlaceInfoPanel(false);
- mPlaceInfoType = key_type;
- togglePlaceInfoPanel(false);
- // Update the active tab
- onTabSelected();
- // Update the buttons at the bottom of the panel
- updateVerbs();
- }
- else if (key_type == CREATE_PICK_TYPE)
- {
- LLUUID item_id = key["item_id"];
-
- LLLandmarksPanel* landmarks_panel =
- dynamic_cast<LLLandmarksPanel*>(mTabContainer->getPanelByName("Landmarks"));
- if (landmarks_panel && item_id.notNull())
- {
- LLLandmark* landmark = LLLandmarkActions::getLandmark(item_id, boost::bind(&LLLandmarksPanel::doCreatePick, landmarks_panel, _1, item_id));
- if (landmark)
- {
- landmarks_panel->doCreatePick(landmark, item_id);
- }
- }
- }
- else // "create_landmark"
- {
- mFilterEditor->clear();
- onFilterEdit("", false);
-
- mPlaceInfoType = key_type;
- mPosGlobal.setZero();
- mItem = NULL;
- mRegionId.setNull();
- togglePlaceInfoPanel(true);
-
- if (mPlaceInfoType == AGENT_INFO_TYPE)
- {
- mPlaceProfile->setInfoType(LLPanelPlaceInfo::AGENT);
- if (gAgent.getRegion())
- {
- mRegionId = gAgent.getRegion()->getRegionID();
- }
- }
- else if (mPlaceInfoType == CREATE_LANDMARK_INFO_TYPE)
- {
- LLUUID dest_folder = key["dest_folder"];
- mLandmarkInfo->setInfoAndCreateLandmark(dest_folder);
-
- if (key.has("x") && key.has("y") && key.has("z"))
- {
- mPosGlobal = LLVector3d(key["x"].asReal(),
- key["y"].asReal(),
- key["z"].asReal());
- }
- else
- {
- mPosGlobal = gAgent.getPositionGlobal();
- }
-
- mLandmarkInfo->displayParcelInfo(LLUUID(), mPosGlobal);
-
- mSaveBtn->setEnabled(false);
- }
- else if (mPlaceInfoType == LANDMARK_INFO_TYPE)
- {
- mLandmarkInfo->setInfoType(LLPanelPlaceInfo::LANDMARK);
-
- LLUUID id = key["id"].asUUID();
- LLInventoryItem* item = gInventory.getItem(id);
- if (!item)
- return;
-
- bool is_editable = gInventory.isObjectDescendentOf(id, gInventory.getRootFolderID())
- && item->getPermissions().allowModifyBy(gAgent.getID());
- mLandmarkInfo->setCanEdit(is_editable);
-
- setItem(item);
- }
- else if (mPlaceInfoType == REMOTE_PLACE_INFO_TYPE)
- {
- if (key.has("id"))
- {
- LLUUID parcel_id = key["id"].asUUID();
- mPlaceProfile->setParcelID(parcel_id);
-
- // query the server to get the global 3D position of this
- // parcel - we need this for teleport/mapping functions.
- mRemoteParcelObserver->setParcelID(parcel_id);
- }
- else
- {
- mPosGlobal = LLVector3d(key["x"].asReal(),
- key["y"].asReal(),
- key["z"].asReal());
- mPlaceProfile->displayParcelInfo(LLUUID(), mPosGlobal);
- }
-
- mPlaceProfile->setInfoType(LLPanelPlaceInfo::PLACE);
- }
- else if (mPlaceInfoType == TELEPORT_HISTORY_INFO_TYPE)
- {
- S32 index = key["id"].asInteger();
-
- const LLTeleportHistoryStorage::slurl_list_t& hist_items =
- LLTeleportHistoryStorage::getInstance()->getItems();
-
- mPosGlobal = hist_items[index].mGlobalPos;
-
- mPlaceProfile->setInfoType(LLPanelPlaceInfo::TELEPORT_HISTORY);
- mPlaceProfile->displayParcelInfo(LLUUID(), mPosGlobal);
- }
-
- updateVerbs();
- }
- }
-
- LLViewerParcelMgr* parcel_mgr = LLViewerParcelMgr::getInstance();
- if (!parcel_mgr)
- return;
-
- mParcelLocalId = parcel_mgr->getAgentParcel()->getLocalID();
-
- // Start using LLViewerParcelMgr for land selection if
- // information about nearby land is requested.
- // Otherwise stop using land selection and deselect land.
- if (mPlaceInfoType == AGENT_INFO_TYPE)
- {
- // We don't know if we are already added to LLViewerParcelMgr observers list
- // so try to remove observer not to add an extra one.
- parcel_mgr->removeObserver(mParcelObserver);
-
- parcel_mgr->addObserver(mParcelObserver);
- parcel_mgr->selectParcelAt(gAgent.getPositionGlobal());
- }
- else
- {
- parcel_mgr->removeObserver(mParcelObserver);
-
- // Clear the reference to selection to allow its removal in deselectUnused().
- mParcel.clear();
-
- if (!parcel_mgr->selectionEmpty())
- {
- parcel_mgr->deselectUnused();
- }
- }
-}
-
-void LLPanelPlaces::setItem(LLInventoryItem* item)
-{
- if (!mLandmarkInfo || !item)
- return;
-
- mItem = item;
-
- LLAssetType::EType item_type = mItem->getActualType();
- if (item_type == LLAssetType::AT_LANDMARK || item_type == LLAssetType::AT_LINK)
- {
- // If the item is a link get a linked item
- if (item_type == LLAssetType::AT_LINK)
- {
- mItem = gInventory.getItem(mItem->getLinkedUUID());
- if (mItem.isNull())
- return;
- }
- }
- else
- {
- return;
- }
-
- // Check if item is in agent's inventory and he has the permission to modify it.
- bool is_landmark_editable = gInventory.isObjectDescendentOf(mItem->getUUID(), gInventory.getRootFolderID()) &&
- mItem->getPermissions().allowModifyBy(gAgent.getID());
-
- mSaveBtn->setEnabled(is_landmark_editable);
-
- if (is_landmark_editable)
- {
- if(!mLandmarkInfo->setLandmarkFolder(mItem->getParentUUID()) && !mItem->getParentUUID().isNull())
- {
- const LLViewerInventoryCategory* cat = gInventory.getCategory(mItem->getParentUUID());
- if (cat)
- {
- std::string cat_fullname = LLPanelLandmarkInfo::getFullFolderName(cat);
- LLComboBox* folderList = mLandmarkInfo->getChild<LLComboBox>("folder_combo");
- folderList->add(cat_fullname, cat->getUUID(), ADD_TOP);
- }
- }
- }
-
- mLandmarkInfo->displayItemInfo(mItem);
-
- LLLandmark* lm = gLandmarkList.getAsset(mItem->getAssetUUID(),
- boost::bind(&LLPanelPlaces::onLandmarkLoaded, this, _1));
- if (lm)
- {
- onLandmarkLoaded(lm);
- }
-}
-
-S32 LLPanelPlaces::notifyParent(const LLSD& info)
-{
- if(info.has("update_verbs"))
- {
- if(mPosGlobal.isExactlyZero())
- {
- mPosGlobal.setVec(info["global_x"], info["global_y"], info["global_z"]);
- }
-
- updateVerbs();
-
- return 1;
- }
- return LLPanel::notifyParent(info);
-}
-
-void LLPanelPlaces::onLandmarkLoaded(LLLandmark* landmark)
-{
- if (!mLandmarkInfo)
- return;
-
- LLUUID region_id;
- landmark->getRegionID(region_id);
- landmark->getGlobalPos(mPosGlobal);
- mLandmarkInfo->displayParcelInfo(region_id, mPosGlobal);
-
- updateVerbs();
-}
-
-void LLPanelPlaces::onFilterEdit(const std::string& search_string, bool force_filter)
-{
- if (!mActivePanel)
- return;
-
- if (force_filter || mActivePanel->getFilterSubString() != search_string)
- {
- std::string string = search_string;
-
- // Searches are case-insensitive
- // but we don't convert the typed string to upper-case so that it can be fed to the web search as-is.
-
- mActivePanel->onSearchEdit(string);
- }
-}
-
-void LLPanelPlaces::onTabSelected()
-{
- mActivePanel = dynamic_cast<LLPanelPlacesTab*>(mTabContainer->getCurrentPanel());
- if (!mActivePanel)
- return;
-
- onFilterEdit(mActivePanel->getFilterSubString(), true);
- mActivePanel->updateVerbs();
-
- // History panel does not support deletion nor creation
- // Hide menus
- bool supports_create = mActivePanel->getCreateMenu() != NULL;
- childSetVisible("add_btn_panel", supports_create);
-
- // favorites and inventory can remove items, history can clear history
- childSetVisible("trash_btn_panel", true);
-
- if (supports_create)
- {
- mRemoveSelectedBtn->setToolTip(getString("tooltip_trash_items"));
- }
- else
- {
- mRemoveSelectedBtn->setToolTip(getString("tooltip_trash_history"));
- }
-}
-
-void LLPanelPlaces::onTeleportButtonClicked()
-{
- LLPanelPlaceInfo* panel = getCurrentInfoPanel();
- if (panel && panel->getVisible())
- {
- if (mPlaceInfoType == LANDMARK_INFO_TYPE)
- {
- if (mItem.isNull())
- {
- LL_WARNS() << "NULL landmark item" << LL_ENDL;
- llassert(mItem.notNull());
- return;
- }
-
- LLSD payload;
- payload["asset_id"] = mItem->getAssetUUID();
- LLSD args;
- args["LOCATION"] = mItem->getName();
- LLNotificationsUtil::add("TeleportFromLandmark", args, payload);
- }
- else if (mPlaceInfoType == AGENT_INFO_TYPE ||
- mPlaceInfoType == REMOTE_PLACE_INFO_TYPE ||
- mPlaceInfoType == TELEPORT_HISTORY_INFO_TYPE)
- {
- LLFloaterWorldMap* worldmap_instance = LLFloaterWorldMap::getInstance();
- if (!mPosGlobal.isExactlyZero() && worldmap_instance)
- {
- gAgent.teleportViaLocation(mPosGlobal);
- worldmap_instance->trackLocation(mPosGlobal);
- }
- }
- }
- else
- {
- if (mActivePanel)
- mActivePanel->onTeleport();
- }
-}
-
-void LLPanelPlaces::onShowOnMapButtonClicked()
-{
- LLPanelPlaceInfo* panel = getCurrentInfoPanel();
- if (panel && panel->getVisible())
- {
- LLFloaterWorldMap* worldmap_instance = LLFloaterWorldMap::getInstance();
- if(!worldmap_instance)
- return;
-
- if (mPlaceInfoType == AGENT_INFO_TYPE ||
- mPlaceInfoType == CREATE_LANDMARK_INFO_TYPE ||
- mPlaceInfoType == REMOTE_PLACE_INFO_TYPE ||
- mPlaceInfoType == TELEPORT_HISTORY_INFO_TYPE)
- {
- if (!mPosGlobal.isExactlyZero())
- {
- worldmap_instance->trackLocation(mPosGlobal);
- LLFloaterReg::showInstance("world_map", "center");
- }
- }
- else if (mPlaceInfoType == LANDMARK_INFO_TYPE)
- {
- if (mItem.isNull())
- {
- LL_WARNS() << "NULL landmark item" << LL_ENDL;
- llassert(mItem.notNull());
- return;
- }
- LLLandmark* landmark = gLandmarkList.getAsset(mItem->getAssetUUID());
- if (!landmark)
- return;
-
- LLVector3d landmark_global_pos;
- if (!landmark->getGlobalPos(landmark_global_pos))
- return;
-
- if (!landmark_global_pos.isExactlyZero())
- {
- worldmap_instance->trackLocation(landmark_global_pos);
- LLFloaterReg::showInstance("world_map", "center");
- }
- }
- }
- else
- {
- if (mActivePanel && mActivePanel->isSingleItemSelected())
- {
- mActivePanel->onShowOnMap();
- }
- else
- {
- LLFloaterWorldMap* worldmap_instance = LLFloaterWorldMap::getInstance();
- LLVector3d global_pos = gAgent.getPositionGlobal();
-
- if (!global_pos.isExactlyZero() && worldmap_instance)
- {
- worldmap_instance->trackLocation(global_pos);
- LLFloaterReg::showInstance("world_map", "center");
- }
- }
- }
-}
-
-void LLPanelPlaces::onEditButtonClicked()
-{
- if (!mLandmarkInfo || isLandmarkEditModeOn)
- return;
-
- isLandmarkEditModeOn = true;
-
- mLandmarkInfo->toggleLandmarkEditMode(true);
-
- updateVerbs();
-}
-
-void LLPanelPlaces::onSaveButtonClicked()
-{
- if (!mLandmarkInfo || mItem.isNull())
- return;
-
- std::string current_title_value = mLandmarkInfo->getLandmarkTitle();
- std::string item_title_value = mItem->getName();
- std::string current_notes_value = mLandmarkInfo->getLandmarkNotes();
- std::string item_notes_value = mItem->getDescription();
-
- LLStringUtil::trim(current_title_value);
- LLStringUtil::trim(current_notes_value);
-
- LLUUID folder_id = mLandmarkInfo->getLandmarkFolder();
- bool change_parent = folder_id != mItem->getParentUUID();
-
- LLPointer<LLViewerInventoryItem> new_item = new LLViewerInventoryItem(mItem);
-
- if (!current_title_value.empty() &&
- (item_title_value != current_title_value || item_notes_value != current_notes_value))
- {
- new_item->rename(current_title_value);
- new_item->setDescription(current_notes_value);
- LLPointer<LLInventoryCallback> cb;
- if (change_parent)
- {
- cb = new LLUpdateLandmarkParent(new_item, folder_id);
- }
- LLInventoryModel::LLCategoryUpdate up(mItem->getParentUUID(), 0);
- gInventory.accountForUpdate(up);
- update_inventory_item(new_item, cb);
- }
- else if (change_parent)
- {
- LLInventoryModel::update_list_t update;
- LLInventoryModel::LLCategoryUpdate old_folder(mItem->getParentUUID(),-1);
- update.push_back(old_folder);
- LLInventoryModel::LLCategoryUpdate new_folder(folder_id, 1);
- update.push_back(new_folder);
- gInventory.accountForUpdate(update);
-
- new_item->setParent(folder_id);
- new_item->updateParentOnServer(false);
- }
-
- gInventory.updateItem(new_item);
- gInventory.notifyObservers();
-
- onCancelButtonClicked();
-}
-
-void LLPanelPlaces::onCancelButtonClicked()
-{
- if (!mLandmarkInfo)
- return;
-
- if (mPlaceInfoType == CREATE_LANDMARK_INFO_TYPE)
- {
- onBackButtonClicked();
- }
- else
- {
- mLandmarkInfo->toggleLandmarkEditMode(false);
- isLandmarkEditModeOn = false;
-
- updateVerbs();
-
- // Reload the landmark properties.
- mLandmarkInfo->displayItemInfo(mItem);
- }
-}
-
-void LLPanelPlaces::onOverflowButtonClicked()
-{
- LLToggleableMenu* menu;
-
- bool is_agent_place_info_visible = mPlaceInfoType == AGENT_INFO_TYPE;
-
- if ((is_agent_place_info_visible ||
- mPlaceInfoType == REMOTE_PLACE_INFO_TYPE ||
- mPlaceInfoType == TELEPORT_HISTORY_INFO_TYPE) && mPlaceMenu != NULL)
- {
- menu = mPlaceMenu;
-
- bool landmark_item_enabled = false;
- LLViewerParcelMgr* parcel_mgr = LLViewerParcelMgr::getInstance();
- if (is_agent_place_info_visible
- && gAgent.getRegion()
- && mRegionId == gAgent.getRegion()->getRegionID()
- && parcel_mgr
- && parcel_mgr->getAgentParcel()->getLocalID() == mParcelLocalId)
- {
- // Floater still shows location identical to agent's position
- landmark_item_enabled = !LLLandmarkActions::landmarkAlreadyExists();
- }
-
- // Enable adding a landmark only for agent current parcel and if
- // there is no landmark already pointing to that parcel in agent's inventory.
- menu->getChild<LLMenuItemCallGL>("landmark")->setEnabled(landmark_item_enabled);
- // STORM-411
- // Creating landmarks for remote locations is impossible.
- // So hide menu item "Make a Landmark" in "Teleport History Profile" panel.
- menu->setItemVisible("landmark", mPlaceInfoType != TELEPORT_HISTORY_INFO_TYPE);
- menu->arrangeAndClear();
- }
- else if (mPlaceInfoType == LANDMARK_INFO_TYPE && mLandmarkMenu != NULL)
- {
- menu = mLandmarkMenu;
-
- bool is_landmark_removable = false;
- if (mItem.notNull())
- {
- const LLUUID& item_id = mItem->getUUID();
- const LLUUID trash_id = gInventory.findCategoryUUIDForType(LLFolderType::FT_TRASH);
- is_landmark_removable = gInventory.isObjectDescendentOf(item_id, gInventory.getRootFolderID()) &&
- !gInventory.isObjectDescendentOf(item_id, trash_id);
- }
-
- menu->getChild<LLMenuItemCallGL>("delete")->setEnabled(is_landmark_removable);
- }
- else
- {
- return;
- }
-
- mOverflowBtn->setMenu(menu, LLMenuButton::MP_TOP_RIGHT);
-}
-
-bool LLPanelPlaces::onOverflowMenuItemEnable(const LLSD& param)
-{
- std::string value = param.asString();
- if("can_create_pick" == value)
- {
- return !LLAgentPicksInfo::getInstance()->isPickLimitReached();
- }
- return true;
-}
-
-void LLPanelPlaces::onOverflowMenuItemClicked(const LLSD& param)
-{
- std::string item = param.asString();
- if (item == "landmark")
- {
- LLSD key;
- key["type"] = CREATE_LANDMARK_INFO_TYPE;
- key["x"] = mPosGlobal.mdV[VX];
- key["y"] = mPosGlobal.mdV[VY];
- key["z"] = mPosGlobal.mdV[VZ];
- onOpen(key);
- }
- else if (item == "copy")
- {
- LLLandmarkActions::getSLURLfromPosGlobal(mPosGlobal, boost::bind(&onSLURLBuilt, _1));
- }
- else if (item == "delete")
- {
- gInventory.removeItem(mItem->getUUID());
-
- onBackButtonClicked();
- }
- else if (item == "pick")
- {
- LLPanelPlaceInfo* panel = getCurrentInfoPanel();
- if (panel)
- {
- panel->createPick(mPosGlobal);
- }
- }
- else if (item == "add_to_favbar")
- {
- if ( mItem.notNull() )
- {
- const LLUUID& favorites_id = gInventory.findCategoryUUIDForType(LLFolderType::FT_FAVORITE);
- if ( favorites_id.notNull() )
- {
- copy_inventory_item(gAgent.getID(),
- mItem->getPermissions().getOwner(),
- mItem->getUUID(),
- favorites_id,
- std::string(),
- LLPointer<LLInventoryCallback>(NULL));
- LL_INFOS() << "Copied inventory item #" << mItem->getUUID() << " to favorites." << LL_ENDL;
- }
- }
- }
-}
-
-void LLPanelPlaces::onBackButtonClicked()
-{
- togglePlaceInfoPanel(false);
-
- // Resetting mPlaceInfoType when Place Info panel is closed.
- mPlaceInfoType = LLStringUtil::null;
-
- isLandmarkEditModeOn = false;
-
- updateVerbs();
-}
-
-void LLPanelPlaces::onGearMenuClick()
-{
- if (mActivePanel)
- {
- LLToggleableMenu* menu = mActivePanel->getSelectionMenu();
- mGearMenuButton->setMenu(menu, LLMenuButton::MP_BOTTOM_LEFT);
- }
-}
-
-void LLPanelPlaces::onSortingMenuClick()
-{
- if (mActivePanel)
- {
- LLToggleableMenu* menu = mActivePanel->getSortingMenu();
- mSortingMenuButton->setMenu(menu, LLMenuButton::MP_BOTTOM_LEFT);
- }
-}
-
-void LLPanelPlaces::onAddMenuClick()
-{
- if (mActivePanel)
- {
- LLToggleableMenu* menu = mActivePanel->getCreateMenu();
- mAddMenuButton->setMenu(menu, LLMenuButton::MP_BOTTOM_LEFT);
- }
-}
-
-void LLPanelPlaces::onRemoveButtonClicked()
-{
- if (mActivePanel)
- {
- mActivePanel->onRemoveSelected();
- }
-}
-
-bool LLPanelPlaces::handleDragAndDropToTrash(bool drop, EDragAndDropType cargo_type, void* cargo_data, EAcceptance* accept)
-{
- if (mActivePanel)
- {
- return mActivePanel->handleDragAndDropToTrash(drop, cargo_type, cargo_data, accept);
- }
- return false;
-}
-
-void LLPanelPlaces::togglePlaceInfoPanel(bool visible)
-{
- if (!mPlaceProfile || !mLandmarkInfo)
- return;
-
- mTabContainer->setVisible(!visible);
- mButtonsContainer->setVisible(visible);
- mFilterContainer->setVisible(!visible);
-
- if (mPlaceInfoType == AGENT_INFO_TYPE ||
- mPlaceInfoType == REMOTE_PLACE_INFO_TYPE ||
- mPlaceInfoType == TELEPORT_HISTORY_INFO_TYPE)
- {
- mPlaceProfile->setVisible(visible);
-
- if (visible)
- {
- mPlaceProfile->resetLocation();
-
- // Do not reset location info until mResetInfoTimer has expired
- // to avoid text blinking.
- mResetInfoTimer.setTimerExpirySec(PLACE_INFO_UPDATE_INTERVAL);
-
- mLandmarkInfo->setVisible(false);
- }
- else if (mPlaceInfoType == AGENT_INFO_TYPE)
- {
- LLViewerParcelMgr::getInstance()->removeObserver(mParcelObserver);
-
- // Clear reference to parcel selection when closing place profile panel.
- // LLViewerParcelMgr removes the selection if it has 1 reference to it.
- mParcel.clear();
- }
- }
- else if (mPlaceInfoType == CREATE_LANDMARK_INFO_TYPE ||
- mPlaceInfoType == LANDMARK_INFO_TYPE ||
- mPlaceInfoType == LANDMARK_TAB_INFO_TYPE)
- {
- mLandmarkInfo->setVisible(visible);
- mPlaceProfile->setVisible(false);
- if (visible)
- {
- mLandmarkInfo->resetLocation();
- }
- else
- {
- std::string tab_panel_name("Landmarks");
- if (mItem.notNull())
- {
- if (gInventory.isObjectDescendentOf(mItem->getUUID(), gInventory.findCategoryUUIDForType(LLFolderType::FT_FAVORITE)))
- {
- tab_panel_name = "Favorites";
- }
- }
-
- LLLandmarksPanel* landmarks_panel = dynamic_cast<LLLandmarksPanel*>(mTabContainer->getPanelByName(tab_panel_name));
- if (landmarks_panel)
- {
- // If a landmark info is being closed we open the landmarks tab
- // and set this landmark selected.
- mTabContainer->selectTabPanel(landmarks_panel);
- if (mItem.notNull())
- {
- landmarks_panel->setItemSelected(mItem->getUUID(), true);
- }
- else
- {
- landmarks_panel->resetSelection();
- }
- }
- }
- }
-}
-
-// virtual
-void LLPanelPlaces::onVisibilityChange(bool new_visibility)
-{
- LLPanel::onVisibilityChange(new_visibility);
-
- if (!new_visibility && mPlaceInfoType == AGENT_INFO_TYPE)
- {
- LLViewerParcelMgr::getInstance()->removeObserver(mParcelObserver);
-
- // Clear reference to parcel selection when closing places panel.
- mParcel.clear();
- }
-}
-
-void LLPanelPlaces::changedParcelSelection()
-{
- if (!mPlaceProfile)
- return;
-
- LLViewerParcelMgr* parcel_mgr = LLViewerParcelMgr::getInstance();
- mParcel = parcel_mgr->getFloatingParcelSelection();
- LLParcel* parcel = mParcel->getParcel();
- LLViewerRegion* region = parcel_mgr->getSelectionRegion();
- if (!region || !parcel)
- return;
-
- LLVector3d prev_pos_global = mPosGlobal;
-
- // If agent is inside the selected parcel show agent's region<X, Y, Z>,
- // otherwise show region<X, Y, Z> of agent's selection point.
- bool is_current_parcel = is_agent_in_selected_parcel(parcel);
- if (is_current_parcel)
- {
- mPosGlobal = gAgent.getPositionGlobal();
- }
- else
- {
- LLVector3d pos_global = gViewerWindow->getLastPick().mPosGlobal;
- if (!pos_global.isExactlyZero())
- {
- mPosGlobal = pos_global;
- }
- }
-
- // Reset location info only if global position has changed
- // and update timer has expired to reduce unnecessary text and icons updates.
- if (prev_pos_global != mPosGlobal && mResetInfoTimer.hasExpired())
- {
- mPlaceProfile->resetLocation();
- mResetInfoTimer.setTimerExpirySec(PLACE_INFO_UPDATE_INTERVAL);
- }
-
- mPlaceProfile->displaySelectedParcelInfo(parcel, region, mPosGlobal, is_current_parcel);
-
- updateVerbs();
-}
-
-void LLPanelPlaces::createTabs()
-{
- if (!(gInventory.isInventoryUsable() && LLTeleportHistory::getInstance() && !mTabsCreated))
- return;
-
- LLFavoritesPanel* favorites_panel = new LLFavoritesPanel();
- if (favorites_panel)
- {
- mTabContainer->addTabPanel(
- LLTabContainer::TabPanelParams().
- panel(favorites_panel).
- label(getString("favorites_tab_title")).
- insert_at(LLTabContainer::END));
- }
-
- LLLandmarksPanel* landmarks_panel = new LLLandmarksPanel();
- if (landmarks_panel)
- {
- mTabContainer->addTabPanel(
- LLTabContainer::TabPanelParams().
- panel(landmarks_panel).
- label(getString("landmarks_tab_title")).
- insert_at(LLTabContainer::END));
- }
-
- LLTeleportHistoryPanel* teleport_history_panel = new LLTeleportHistoryPanel();
- if (teleport_history_panel)
- {
- mTabContainer->addTabPanel(
- LLTabContainer::TabPanelParams().
- panel(teleport_history_panel).
- label(getString("teleport_history_tab_title")).
- insert_at(LLTabContainer::END));
- }
-
- mTabContainer->selectFirstTab();
-
- mActivePanel = dynamic_cast<LLPanelPlacesTab*>(mTabContainer->getCurrentPanel());
-
- if (mActivePanel)
- {
- // Filter applied to show all items.
- mActivePanel->onSearchEdit(mActivePanel->getFilterSubString());
-
- // History panel does not support deletion nor creation
- // Hide menus
- bool supports_create = mActivePanel->getCreateMenu() != NULL;
- childSetVisible("add_btn_panel", supports_create);
-
- // favorites and inventory can remove items, history can clear history
- childSetVisible("trash_btn_panel", true);
-
- if (supports_create)
- {
- mRemoveSelectedBtn->setToolTip(getString("tooltip_trash_items"));
- }
- else
- {
- mRemoveSelectedBtn->setToolTip(getString("tooltip_trash_history"));
- }
-
- mActivePanel->setRemoveBtn(mRemoveSelectedBtn);
- mActivePanel->updateVerbs();
- }
-
- mTabsCreated = true;
-}
-
-void LLPanelPlaces::changedGlobalPos(const LLVector3d &global_pos)
-{
- mPosGlobal = global_pos;
- updateVerbs();
-}
-
-void LLPanelPlaces::showAddedLandmarkInfo(const uuid_set_t& items)
-{
- for (uuid_set_t::const_iterator item_iter = items.begin();
- item_iter != items.end();
- ++item_iter)
- {
- const LLUUID& item_id = (*item_iter);
- if(!highlight_offered_object(item_id))
- {
- continue;
- }
-
- LLInventoryItem* item = gInventory.getItem(item_id);
-
- llassert(item);
- if (item && (LLAssetType::AT_LANDMARK == item->getType()) )
- {
- // Created landmark is passed to Places panel to allow its editing.
- // If the panel is closed we don't reopen it until created landmark is loaded.
- if("create_landmark" == getPlaceInfoType() && !getItem())
- {
- setItem(item);
- }
- }
- }
-}
-
-void LLPanelPlaces::updateVerbs()
-{
- bool is_place_info_visible;
-
- LLPanelPlaceInfo* panel = getCurrentInfoPanel();
- if (panel)
- {
- is_place_info_visible = panel->getVisible();
- }
- else
- {
- is_place_info_visible = false;
- }
-
- bool is_agent_place_info_visible = mPlaceInfoType == AGENT_INFO_TYPE;
- bool is_create_landmark_visible = mPlaceInfoType == CREATE_LANDMARK_INFO_TYPE;
-
- bool have_3d_pos = ! mPosGlobal.isExactlyZero();
-
- mTeleportBtn->setVisible(!is_create_landmark_visible && !isLandmarkEditModeOn);
- mShowOnMapBtn->setVisible(!is_create_landmark_visible && !isLandmarkEditModeOn);
- mSaveBtn->setVisible(isLandmarkEditModeOn);
- mCancelBtn->setVisible(isLandmarkEditModeOn);
- mCloseBtn->setVisible(is_create_landmark_visible && !isLandmarkEditModeOn);
-
- bool show_options_btn = is_place_info_visible && !is_create_landmark_visible && !isLandmarkEditModeOn;
- mOverflowBtn->setVisible(show_options_btn);
- getChild<LLLayoutPanel>("lp_options")->setVisible(show_options_btn);
- getChild<LLLayoutPanel>("lp2")->setVisible(!show_options_btn);
-
- if (is_place_info_visible)
- {
- mShowOnMapBtn->setEnabled(have_3d_pos);
-
- if (is_agent_place_info_visible)
- {
- // We don't need to teleport to the current location
- // so check if the location is not within the current parcel.
- mTeleportBtn->setEnabled(have_3d_pos &&
- !LLViewerParcelMgr::getInstance()->inAgentParcel(mPosGlobal));
- }
- else if (mPlaceInfoType == LANDMARK_INFO_TYPE || mPlaceInfoType == REMOTE_PLACE_INFO_TYPE)
- {
- mTeleportBtn->setEnabled(have_3d_pos);
- }
- }
- else
- {
- if (mActivePanel)
- mActivePanel->updateVerbs();
- }
-}
-
-LLPanelPlaceInfo* LLPanelPlaces::getCurrentInfoPanel()
-{
- if (mPlaceInfoType == AGENT_INFO_TYPE ||
- mPlaceInfoType == REMOTE_PLACE_INFO_TYPE ||
- mPlaceInfoType == TELEPORT_HISTORY_INFO_TYPE)
- {
- return mPlaceProfile;
- }
- else if (mPlaceInfoType == CREATE_LANDMARK_INFO_TYPE ||
- mPlaceInfoType == LANDMARK_INFO_TYPE ||
- mPlaceInfoType == LANDMARK_TAB_INFO_TYPE)
- {
- return mLandmarkInfo;
- }
-
- return NULL;
-}
-
-static bool is_agent_in_selected_parcel(LLParcel* parcel)
-{
- LLViewerParcelMgr* parcel_mgr = LLViewerParcelMgr::getInstance();
-
- LLViewerRegion* region = parcel_mgr->getSelectionRegion();
- if (!region || !parcel)
- return false;
-
- return region == gAgent.getRegion() &&
- parcel->getLocalID() == parcel_mgr->getAgentParcel()->getLocalID();
-}
-
-static void onSLURLBuilt(std::string& slurl)
-{
- LLView::getWindow()->copyTextToClipboard(utf8str_to_wstring(slurl));
-
- LLSD args;
- args["SLURL"] = slurl;
-
- LLNotificationsUtil::add("CopySLURL", args);
-}
+/**
+ * @file llpanelplaces.cpp
+ * @brief Side Bar "Places" panel
+ *
+ * $LicenseInfo:firstyear=2009&license=viewerlgpl$
+ * Second Life Viewer Source Code
+ * Copyright (C) 2010, 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 "llpanelplaces.h"
+
+#include "llassettype.h"
+#include "lltimer.h"
+
+#include "llinventory.h"
+#include "lllandmark.h"
+#include "llparcel.h"
+
+#include "llcombobox.h"
+#include "llfiltereditor.h"
+#include "llfirstuse.h"
+#include "llfloaterreg.h"
+#include "llfloatersidepanelcontainer.h"
+#include "llmenubutton.h"
+#include "llnotificationsutil.h"
+#include "lltabcontainer.h"
+#include "lltexteditor.h"
+#include "lltrans.h"
+#include "lluictrlfactory.h"
+
+#include "llwindow.h"
+
+#include "llagent.h"
+#include "llagentpicksinfo.h"
+#include "llavatarpropertiesprocessor.h"
+#include "llcommandhandler.h"
+#include "lldndbutton.h"
+#include "llfloaterworldmap.h"
+#include "llinventorybridge.h"
+#include "llinventoryobserver.h"
+#include "llinventorymodel.h"
+#include "lllandmarkactions.h"
+#include "lllandmarklist.h"
+#include "lllayoutstack.h"
+#include "llpanellandmarkinfo.h"
+#include "llpanellandmarks.h"
+#include "llpanelplaceprofile.h"
+#include "llpanelteleporthistory.h"
+#include "llremoteparcelrequest.h"
+#include "llteleporthistorystorage.h"
+#include "lltoggleablemenu.h"
+#include "llviewerinventory.h"
+#include "llviewermenu.h"
+#include "llviewermessage.h"
+#include "llviewerparcelmgr.h"
+#include "llviewerregion.h"
+#include "llviewerwindow.h"
+
+// Constants
+static const F32 PLACE_INFO_UPDATE_INTERVAL = 3.0;
+static const std::string AGENT_INFO_TYPE = "agent";
+static const std::string CREATE_LANDMARK_INFO_TYPE = "create_landmark";
+static const std::string CREATE_PICK_TYPE = "create_pick";
+static const std::string LANDMARK_INFO_TYPE = "landmark";
+static const std::string REMOTE_PLACE_INFO_TYPE = "remote_place";
+static const std::string TELEPORT_HISTORY_INFO_TYPE = "teleport_history";
+static const std::string LANDMARK_TAB_INFO_TYPE = "open_landmark_tab";
+
+// Support for secondlife:///app/parcel/{UUID}/about SLapps
+class LLParcelHandler : public LLCommandHandler
+{
+public:
+ // requires trusted browser to trigger
+ LLParcelHandler() : LLCommandHandler("parcel", UNTRUSTED_THROTTLE) { }
+ bool handle(const LLSD& params,
+ const LLSD& query_map,
+ const std::string& grid,
+ LLMediaCtrl* web)
+ {
+ if (params.size() < 2)
+ {
+ return false;
+ }
+
+ if (!LLUI::getInstance()->mSettingGroups["config"]->getBOOL("EnablePlaceProfile"))
+ {
+ LLNotificationsUtil::add("NoPlaceInfo", LLSD(), LLSD(), std::string("SwitchToStandardSkinAndQuit"));
+ return true;
+ }
+
+ LLUUID parcel_id;
+ if (!parcel_id.set(params[0], false))
+ {
+ return false;
+ }
+ if (params[1].asString() == "about")
+ {
+ if (parcel_id.notNull())
+ {
+ LLSD key;
+ key["type"] = "remote_place";
+ key["id"] = parcel_id;
+ LLFloaterSidePanelContainer::showPanel("places", key);
+ return true;
+ }
+ }
+ return false;
+ }
+};
+LLParcelHandler gParcelHandler;
+
+// Helper functions
+static bool is_agent_in_selected_parcel(LLParcel* parcel);
+static void onSLURLBuilt(std::string& slurl);
+
+//Observer classes
+class LLPlacesParcelObserver : public LLParcelObserver
+{
+public:
+ LLPlacesParcelObserver(LLPanelPlaces* places_panel) :
+ LLParcelObserver(),
+ mPlaces(places_panel)
+ {}
+
+ /*virtual*/ void changed()
+ {
+ if (mPlaces)
+ mPlaces->changedParcelSelection();
+ }
+
+private:
+ LLPanelPlaces* mPlaces;
+};
+
+class LLPlacesInventoryObserver : public LLInventoryAddedObserver
+{
+public:
+ LLPlacesInventoryObserver(LLPanelPlaces* places_panel) :
+ mPlaces(places_panel)
+ {}
+
+ /*virtual*/ void changed(U32 mask)
+ {
+ LLInventoryAddedObserver::changed(mask);
+
+ if (mPlaces && !mPlaces->tabsCreated())
+ {
+ mPlaces->createTabs();
+ }
+ }
+
+protected:
+ /*virtual*/ void done()
+ {
+ mPlaces->showAddedLandmarkInfo(gInventory.getAddedIDs());
+ }
+
+private:
+ LLPanelPlaces* mPlaces;
+};
+
+class LLPlacesRemoteParcelInfoObserver : public LLRemoteParcelInfoObserver
+{
+public:
+ LLPlacesRemoteParcelInfoObserver(LLPanelPlaces* places_panel) :
+ LLRemoteParcelInfoObserver(),
+ mPlaces(places_panel)
+ {}
+
+ ~LLPlacesRemoteParcelInfoObserver()
+ {
+ // remove any in-flight observers
+ std::set<LLUUID>::iterator it;
+ for (it = mParcelIDs.begin(); it != mParcelIDs.end(); ++it)
+ {
+ const LLUUID &id = *it;
+ LLRemoteParcelInfoProcessor::getInstance()->removeObserver(id, this);
+ }
+ mParcelIDs.clear();
+ }
+
+ /*virtual*/ void processParcelInfo(const LLParcelData& parcel_data)
+ {
+ if (mPlaces)
+ {
+ mPlaces->changedGlobalPos(LLVector3d(parcel_data.global_x,
+ parcel_data.global_y,
+ parcel_data.global_z));
+ }
+
+ mParcelIDs.erase(parcel_data.parcel_id);
+ LLRemoteParcelInfoProcessor::getInstance()->removeObserver(parcel_data.parcel_id, this);
+ }
+ /*virtual*/ void setParcelID(const LLUUID& parcel_id)
+ {
+ if (!parcel_id.isNull())
+ {
+ mParcelIDs.insert(parcel_id);
+ LLRemoteParcelInfoProcessor::getInstance()->addObserver(parcel_id, this);
+ LLRemoteParcelInfoProcessor::getInstance()->sendParcelInfoRequest(parcel_id);
+ }
+ }
+ /*virtual*/ void setErrorStatus(S32 status, const std::string& reason)
+ {
+ LL_ERRS() << "Can't complete remote parcel request. Http Status: "
+ << status << ". Reason : " << reason << LL_ENDL;
+ }
+
+private:
+ std::set<LLUUID> mParcelIDs;
+ LLPanelPlaces* mPlaces;
+};
+
+
+static LLPanelInjector<LLPanelPlaces> t_places("panel_places");
+
+LLPanelPlaces::LLPanelPlaces()
+ : LLPanel(),
+ mActivePanel(NULL),
+ mFilterEditor(NULL),
+ mPlaceProfile(NULL),
+ mLandmarkInfo(NULL),
+ mItem(NULL),
+ mPlaceMenu(NULL),
+ mLandmarkMenu(NULL),
+ mPosGlobal(),
+ isLandmarkEditModeOn(false),
+ mTabsCreated(false)
+{
+ mParcelObserver = new LLPlacesParcelObserver(this);
+ mInventoryObserver = new LLPlacesInventoryObserver(this);
+ mRemoteParcelObserver = new LLPlacesRemoteParcelInfoObserver(this);
+
+ gInventory.addObserver(mInventoryObserver);
+
+ mAgentParcelChangedConnection = gAgent.addParcelChangedCallback(
+ boost::bind(&LLPanelPlaces::updateVerbs, this));
+
+ //buildFromFile( "panel_places.xml"); // Called from LLRegisterPanelClass::defaultPanelClassBuilder()
+}
+
+LLPanelPlaces::~LLPanelPlaces()
+{
+ if (gInventory.containsObserver(mInventoryObserver))
+ gInventory.removeObserver(mInventoryObserver);
+
+ LLViewerParcelMgr::getInstance()->removeObserver(mParcelObserver);
+
+ delete mInventoryObserver;
+ delete mParcelObserver;
+ delete mRemoteParcelObserver;
+
+ if (mAgentParcelChangedConnection.connected())
+ {
+ mAgentParcelChangedConnection.disconnect();
+ }
+}
+
+bool LLPanelPlaces::postBuild()
+{
+ mTeleportBtn = getChild<LLButton>("teleport_btn");
+ mTeleportBtn->setClickedCallback(boost::bind(&LLPanelPlaces::onTeleportButtonClicked, this));
+
+ mShowOnMapBtn = getChild<LLButton>("map_btn");
+ mShowOnMapBtn->setClickedCallback(boost::bind(&LLPanelPlaces::onShowOnMapButtonClicked, this));
+
+ mSaveBtn = getChild<LLButton>("save_btn");
+ mSaveBtn->setClickedCallback(boost::bind(&LLPanelPlaces::onSaveButtonClicked, this));
+
+ mCancelBtn = getChild<LLButton>("cancel_btn");
+ mCancelBtn->setClickedCallback(boost::bind(&LLPanelPlaces::onCancelButtonClicked, this));
+
+ mCloseBtn = getChild<LLButton>("close_btn");
+ mCloseBtn->setClickedCallback(boost::bind(&LLPanelPlaces::onBackButtonClicked, this));
+
+ mOverflowBtn = getChild<LLMenuButton>("overflow_btn");
+ mOverflowBtn->setMouseDownCallback(boost::bind(&LLPanelPlaces::onOverflowButtonClicked, this));
+
+ mGearMenuButton = getChild<LLMenuButton>("options_gear_btn");
+ mGearMenuButton->setMouseDownCallback(boost::bind(&LLPanelPlaces::onGearMenuClick, this));
+
+ mSortingMenuButton = getChild<LLMenuButton>("sorting_menu_btn");
+ mSortingMenuButton->setMouseDownCallback(boost::bind(&LLPanelPlaces::onSortingMenuClick, this));
+
+ mAddMenuButton = getChild<LLMenuButton>("add_menu_btn");
+ mAddMenuButton->setMouseDownCallback(boost::bind(&LLPanelPlaces::onAddMenuClick, this));
+
+ mRemoveSelectedBtn = getChild<LLButton>("trash_btn");
+ mRemoveSelectedBtn->setClickedCallback(boost::bind(&LLPanelPlaces::onRemoveButtonClicked, this));
+
+ LLDragAndDropButton* trash_btn = (LLDragAndDropButton*)mRemoveSelectedBtn;
+ trash_btn->setDragAndDropHandler(boost::bind(&LLPanelPlaces::handleDragAndDropToTrash, this
+ , _4 // bool drop
+ , _5 // EDragAndDropType cargo_type
+ , _6 // void* cargo_data
+ , _7 // EAcceptance* accept
+ ));
+
+ LLUICtrl::CommitCallbackRegistry::ScopedRegistrar registrar;
+ registrar.add("Places.OverflowMenu.Action", boost::bind(&LLPanelPlaces::onOverflowMenuItemClicked, this, _2));
+ LLUICtrl::EnableCallbackRegistry::ScopedRegistrar enable_registrar;
+ enable_registrar.add("Places.OverflowMenu.Enable", boost::bind(&LLPanelPlaces::onOverflowMenuItemEnable, this, _2));
+
+ mPlaceMenu = LLUICtrlFactory::getInstance()->createFromFile<LLToggleableMenu>("menu_place.xml", gMenuHolder, LLViewerMenuHolderGL::child_registry_t::instance());
+ if (mPlaceMenu)
+ {
+ mPlaceMenu->setAlwaysShowMenu(true);
+ }
+ else
+ {
+ LL_WARNS() << "Error loading Place menu" << LL_ENDL;
+ }
+
+ mLandmarkMenu = LLUICtrlFactory::getInstance()->createFromFile<LLToggleableMenu>("menu_landmark.xml", gMenuHolder, LLViewerMenuHolderGL::child_registry_t::instance());
+ if (!mLandmarkMenu)
+ {
+ LL_WARNS() << "Error loading Landmark menu" << LL_ENDL;
+ }
+
+ mTabContainer = getChild<LLTabContainer>("Places Tabs");
+ if (mTabContainer)
+ {
+ mTabContainer->setCommitCallback(boost::bind(&LLPanelPlaces::onTabSelected, this));
+ }
+
+ mButtonsContainer = getChild<LLPanel>("button_layout_panel");
+ mButtonsContainer->setVisible(false);
+ mFilterContainer = getChild<LLLayoutStack>("top_menu_panel");
+
+ mFilterEditor = getChild<LLFilterEditor>("Filter");
+ if (mFilterEditor)
+ {
+ //when list item is being clicked the filter editor looses focus
+ //committing on focus lost leads to detaching list items
+ //BUT a detached list item cannot be made selected and must not be clicked onto
+ mFilterEditor->setCommitOnFocusLost(false);
+
+ mFilterEditor->setCommitCallback(boost::bind(&LLPanelPlaces::onFilterEdit, this, _2, false));
+ }
+
+ mPlaceProfile = findChild<LLPanelPlaceProfile>("panel_place_profile");
+ mLandmarkInfo = findChild<LLPanelLandmarkInfo>("panel_landmark_info");
+ if (!mPlaceProfile || !mLandmarkInfo)
+ return false;
+
+ mPlaceProfileBackBtn = mPlaceProfile->getChild<LLButton>("back_btn");
+ mPlaceProfileBackBtn->setClickedCallback(boost::bind(&LLPanelPlaces::onBackButtonClicked, this));
+
+ mLandmarkInfo->getChild<LLButton>("back_btn")->setClickedCallback(boost::bind(&LLPanelPlaces::onBackButtonClicked, this));
+
+ LLLineEditor* title_editor = mLandmarkInfo->getChild<LLLineEditor>("title_editor");
+ title_editor->setKeystrokeCallback(boost::bind(&LLPanelPlaces::onEditButtonClicked, this), NULL);
+
+ LLTextEditor* notes_editor = mLandmarkInfo->getChild<LLTextEditor>("notes_editor");
+ notes_editor->setKeystrokeCallback(boost::bind(&LLPanelPlaces::onEditButtonClicked, this));
+
+ LLComboBox* folder_combo = mLandmarkInfo->getChild<LLComboBox>("folder_combo");
+ folder_combo->setCommitCallback(boost::bind(&LLPanelPlaces::onEditButtonClicked, this));
+
+ LLButton* edit_btn = mLandmarkInfo->getChild<LLButton>("edit_btn");
+ edit_btn->setCommitCallback(boost::bind(&LLPanelPlaces::onEditButtonClicked, this));
+
+ createTabs();
+ updateVerbs();
+
+ return true;
+}
+
+void LLPanelPlaces::onOpen(const LLSD& key)
+{
+ if (!mPlaceProfile || !mLandmarkInfo)
+ return;
+
+ if (key.size() != 0)
+ {
+ isLandmarkEditModeOn = false;
+ std::string key_type = key["type"].asString();
+ if (key_type == LANDMARK_TAB_INFO_TYPE)
+ {
+ // Small hack: We need to toggle twice. The first toggle moves from the Landmark
+ // or Teleport History info panel to the Landmark or Teleport History list panel.
+ // For this first toggle, the mPlaceInfoType should be the one previously used so
+ // that the state can be corretly set.
+ // The second toggle forces the list to be set to Landmark.
+ // This avoids extracting and duplicating all the state logic from togglePlaceInfoPanel()
+ // here or some specific private method
+ togglePlaceInfoPanel(false);
+ mPlaceInfoType = key_type;
+ togglePlaceInfoPanel(false);
+ // Update the active tab
+ onTabSelected();
+ // Update the buttons at the bottom of the panel
+ updateVerbs();
+ }
+ else if (key_type == CREATE_PICK_TYPE)
+ {
+ LLUUID item_id = key["item_id"];
+
+ LLLandmarksPanel* landmarks_panel =
+ dynamic_cast<LLLandmarksPanel*>(mTabContainer->getPanelByName("Landmarks"));
+ if (landmarks_panel && item_id.notNull())
+ {
+ LLLandmark* landmark = LLLandmarkActions::getLandmark(item_id, boost::bind(&LLLandmarksPanel::doCreatePick, landmarks_panel, _1, item_id));
+ if (landmark)
+ {
+ landmarks_panel->doCreatePick(landmark, item_id);
+ }
+ }
+ }
+ else // "create_landmark"
+ {
+ mFilterEditor->clear();
+ onFilterEdit("", false);
+
+ mPlaceInfoType = key_type;
+ mPosGlobal.setZero();
+ mItem = NULL;
+ mRegionId.setNull();
+ togglePlaceInfoPanel(true);
+
+ if (mPlaceInfoType == AGENT_INFO_TYPE)
+ {
+ mPlaceProfile->setInfoType(LLPanelPlaceInfo::AGENT);
+ if (gAgent.getRegion())
+ {
+ mRegionId = gAgent.getRegion()->getRegionID();
+ }
+ }
+ else if (mPlaceInfoType == CREATE_LANDMARK_INFO_TYPE)
+ {
+ LLUUID dest_folder = key["dest_folder"];
+ mLandmarkInfo->setInfoAndCreateLandmark(dest_folder);
+
+ if (key.has("x") && key.has("y") && key.has("z"))
+ {
+ mPosGlobal = LLVector3d(key["x"].asReal(),
+ key["y"].asReal(),
+ key["z"].asReal());
+ }
+ else
+ {
+ mPosGlobal = gAgent.getPositionGlobal();
+ }
+
+ mLandmarkInfo->displayParcelInfo(LLUUID(), mPosGlobal);
+
+ mSaveBtn->setEnabled(false);
+ }
+ else if (mPlaceInfoType == LANDMARK_INFO_TYPE)
+ {
+ mLandmarkInfo->setInfoType(LLPanelPlaceInfo::LANDMARK);
+
+ LLUUID id = key["id"].asUUID();
+ LLInventoryItem* item = gInventory.getItem(id);
+ if (!item)
+ return;
+
+ bool is_editable = gInventory.isObjectDescendentOf(id, gInventory.getRootFolderID())
+ && item->getPermissions().allowModifyBy(gAgent.getID());
+ mLandmarkInfo->setCanEdit(is_editable);
+
+ setItem(item);
+ }
+ else if (mPlaceInfoType == REMOTE_PLACE_INFO_TYPE)
+ {
+ if (key.has("id"))
+ {
+ LLUUID parcel_id = key["id"].asUUID();
+ mPlaceProfile->setParcelID(parcel_id);
+
+ // query the server to get the global 3D position of this
+ // parcel - we need this for teleport/mapping functions.
+ mRemoteParcelObserver->setParcelID(parcel_id);
+ }
+ else
+ {
+ mPosGlobal = LLVector3d(key["x"].asReal(),
+ key["y"].asReal(),
+ key["z"].asReal());
+ mPlaceProfile->displayParcelInfo(LLUUID(), mPosGlobal);
+ }
+
+ mPlaceProfile->setInfoType(LLPanelPlaceInfo::PLACE);
+ }
+ else if (mPlaceInfoType == TELEPORT_HISTORY_INFO_TYPE)
+ {
+ S32 index = key["id"].asInteger();
+
+ const LLTeleportHistoryStorage::slurl_list_t& hist_items =
+ LLTeleportHistoryStorage::getInstance()->getItems();
+
+ mPosGlobal = hist_items[index].mGlobalPos;
+
+ mPlaceProfile->setInfoType(LLPanelPlaceInfo::TELEPORT_HISTORY);
+ mPlaceProfile->displayParcelInfo(LLUUID(), mPosGlobal);
+ }
+
+ updateVerbs();
+ }
+ }
+
+ LLViewerParcelMgr* parcel_mgr = LLViewerParcelMgr::getInstance();
+ if (!parcel_mgr)
+ return;
+
+ mParcelLocalId = parcel_mgr->getAgentParcel()->getLocalID();
+
+ // Start using LLViewerParcelMgr for land selection if
+ // information about nearby land is requested.
+ // Otherwise stop using land selection and deselect land.
+ if (mPlaceInfoType == AGENT_INFO_TYPE)
+ {
+ // We don't know if we are already added to LLViewerParcelMgr observers list
+ // so try to remove observer not to add an extra one.
+ parcel_mgr->removeObserver(mParcelObserver);
+
+ parcel_mgr->addObserver(mParcelObserver);
+ parcel_mgr->selectParcelAt(gAgent.getPositionGlobal());
+ }
+ else
+ {
+ parcel_mgr->removeObserver(mParcelObserver);
+
+ // Clear the reference to selection to allow its removal in deselectUnused().
+ mParcel.clear();
+
+ if (!parcel_mgr->selectionEmpty())
+ {
+ parcel_mgr->deselectUnused();
+ }
+ }
+}
+
+void LLPanelPlaces::setItem(LLInventoryItem* item)
+{
+ if (!mLandmarkInfo || !item)
+ return;
+
+ mItem = item;
+
+ LLAssetType::EType item_type = mItem->getActualType();
+ if (item_type == LLAssetType::AT_LANDMARK || item_type == LLAssetType::AT_LINK)
+ {
+ // If the item is a link get a linked item
+ if (item_type == LLAssetType::AT_LINK)
+ {
+ mItem = gInventory.getItem(mItem->getLinkedUUID());
+ if (mItem.isNull())
+ return;
+ }
+ }
+ else
+ {
+ return;
+ }
+
+ // Check if item is in agent's inventory and he has the permission to modify it.
+ bool is_landmark_editable = gInventory.isObjectDescendentOf(mItem->getUUID(), gInventory.getRootFolderID()) &&
+ mItem->getPermissions().allowModifyBy(gAgent.getID());
+
+ mSaveBtn->setEnabled(is_landmark_editable);
+
+ if (is_landmark_editable)
+ {
+ if(!mLandmarkInfo->setLandmarkFolder(mItem->getParentUUID()) && !mItem->getParentUUID().isNull())
+ {
+ const LLViewerInventoryCategory* cat = gInventory.getCategory(mItem->getParentUUID());
+ if (cat)
+ {
+ std::string cat_fullname = LLPanelLandmarkInfo::getFullFolderName(cat);
+ LLComboBox* folderList = mLandmarkInfo->getChild<LLComboBox>("folder_combo");
+ folderList->add(cat_fullname, cat->getUUID(), ADD_TOP);
+ }
+ }
+ }
+
+ mLandmarkInfo->displayItemInfo(mItem);
+
+ LLLandmark* lm = gLandmarkList.getAsset(mItem->getAssetUUID(),
+ boost::bind(&LLPanelPlaces::onLandmarkLoaded, this, _1));
+ if (lm)
+ {
+ onLandmarkLoaded(lm);
+ }
+}
+
+S32 LLPanelPlaces::notifyParent(const LLSD& info)
+{
+ if(info.has("update_verbs"))
+ {
+ if(mPosGlobal.isExactlyZero())
+ {
+ mPosGlobal.setVec(info["global_x"], info["global_y"], info["global_z"]);
+ }
+
+ updateVerbs();
+
+ return 1;
+ }
+ return LLPanel::notifyParent(info);
+}
+
+void LLPanelPlaces::onLandmarkLoaded(LLLandmark* landmark)
+{
+ if (!mLandmarkInfo)
+ return;
+
+ LLUUID region_id;
+ landmark->getRegionID(region_id);
+ landmark->getGlobalPos(mPosGlobal);
+ mLandmarkInfo->displayParcelInfo(region_id, mPosGlobal);
+
+ updateVerbs();
+}
+
+void LLPanelPlaces::onFilterEdit(const std::string& search_string, bool force_filter)
+{
+ if (!mActivePanel)
+ return;
+
+ if (force_filter || mActivePanel->getFilterSubString() != search_string)
+ {
+ std::string string = search_string;
+
+ // Searches are case-insensitive
+ // but we don't convert the typed string to upper-case so that it can be fed to the web search as-is.
+
+ mActivePanel->onSearchEdit(string);
+ }
+}
+
+void LLPanelPlaces::onTabSelected()
+{
+ mActivePanel = dynamic_cast<LLPanelPlacesTab*>(mTabContainer->getCurrentPanel());
+ if (!mActivePanel)
+ return;
+
+ onFilterEdit(mActivePanel->getFilterSubString(), true);
+ mActivePanel->updateVerbs();
+
+ // History panel does not support deletion nor creation
+ // Hide menus
+ bool supports_create = mActivePanel->getCreateMenu() != NULL;
+ childSetVisible("add_btn_panel", supports_create);
+
+ // favorites and inventory can remove items, history can clear history
+ childSetVisible("trash_btn_panel", true);
+
+ if (supports_create)
+ {
+ mRemoveSelectedBtn->setToolTip(getString("tooltip_trash_items"));
+ }
+ else
+ {
+ mRemoveSelectedBtn->setToolTip(getString("tooltip_trash_history"));
+ }
+}
+
+void LLPanelPlaces::onTeleportButtonClicked()
+{
+ LLPanelPlaceInfo* panel = getCurrentInfoPanel();
+ if (panel && panel->getVisible())
+ {
+ if (mPlaceInfoType == LANDMARK_INFO_TYPE)
+ {
+ if (mItem.isNull())
+ {
+ LL_WARNS() << "NULL landmark item" << LL_ENDL;
+ llassert(mItem.notNull());
+ return;
+ }
+
+ LLSD payload;
+ payload["asset_id"] = mItem->getAssetUUID();
+ LLSD args;
+ args["LOCATION"] = mItem->getName();
+ LLNotificationsUtil::add("TeleportFromLandmark", args, payload);
+ }
+ else if (mPlaceInfoType == AGENT_INFO_TYPE ||
+ mPlaceInfoType == REMOTE_PLACE_INFO_TYPE ||
+ mPlaceInfoType == TELEPORT_HISTORY_INFO_TYPE)
+ {
+ LLFloaterWorldMap* worldmap_instance = LLFloaterWorldMap::getInstance();
+ if (!mPosGlobal.isExactlyZero() && worldmap_instance)
+ {
+ gAgent.teleportViaLocation(mPosGlobal);
+ worldmap_instance->trackLocation(mPosGlobal);
+ }
+ }
+ }
+ else
+ {
+ if (mActivePanel)
+ mActivePanel->onTeleport();
+ }
+}
+
+void LLPanelPlaces::onShowOnMapButtonClicked()
+{
+ LLPanelPlaceInfo* panel = getCurrentInfoPanel();
+ if (panel && panel->getVisible())
+ {
+ LLFloaterWorldMap* worldmap_instance = LLFloaterWorldMap::getInstance();
+ if(!worldmap_instance)
+ return;
+
+ if (mPlaceInfoType == AGENT_INFO_TYPE ||
+ mPlaceInfoType == CREATE_LANDMARK_INFO_TYPE ||
+ mPlaceInfoType == REMOTE_PLACE_INFO_TYPE ||
+ mPlaceInfoType == TELEPORT_HISTORY_INFO_TYPE)
+ {
+ if (!mPosGlobal.isExactlyZero())
+ {
+ worldmap_instance->trackLocation(mPosGlobal);
+ LLFloaterReg::showInstance("world_map", "center");
+ }
+ }
+ else if (mPlaceInfoType == LANDMARK_INFO_TYPE)
+ {
+ if (mItem.isNull())
+ {
+ LL_WARNS() << "NULL landmark item" << LL_ENDL;
+ llassert(mItem.notNull());
+ return;
+ }
+ LLLandmark* landmark = gLandmarkList.getAsset(mItem->getAssetUUID());
+ if (!landmark)
+ return;
+
+ LLVector3d landmark_global_pos;
+ if (!landmark->getGlobalPos(landmark_global_pos))
+ return;
+
+ if (!landmark_global_pos.isExactlyZero())
+ {
+ worldmap_instance->trackLocation(landmark_global_pos);
+ LLFloaterReg::showInstance("world_map", "center");
+ }
+ }
+ }
+ else
+ {
+ if (mActivePanel && mActivePanel->isSingleItemSelected())
+ {
+ mActivePanel->onShowOnMap();
+ }
+ else
+ {
+ LLFloaterWorldMap* worldmap_instance = LLFloaterWorldMap::getInstance();
+ LLVector3d global_pos = gAgent.getPositionGlobal();
+
+ if (!global_pos.isExactlyZero() && worldmap_instance)
+ {
+ worldmap_instance->trackLocation(global_pos);
+ LLFloaterReg::showInstance("world_map", "center");
+ }
+ }
+ }
+}
+
+void LLPanelPlaces::onEditButtonClicked()
+{
+ if (!mLandmarkInfo || isLandmarkEditModeOn)
+ return;
+
+ isLandmarkEditModeOn = true;
+
+ mLandmarkInfo->toggleLandmarkEditMode(true);
+
+ updateVerbs();
+}
+
+void LLPanelPlaces::onSaveButtonClicked()
+{
+ if (!mLandmarkInfo || mItem.isNull())
+ return;
+
+ std::string current_title_value = mLandmarkInfo->getLandmarkTitle();
+ std::string item_title_value = mItem->getName();
+ std::string current_notes_value = mLandmarkInfo->getLandmarkNotes();
+ std::string item_notes_value = mItem->getDescription();
+
+ LLStringUtil::trim(current_title_value);
+ LLStringUtil::trim(current_notes_value);
+
+ LLUUID folder_id = mLandmarkInfo->getLandmarkFolder();
+ bool change_parent = folder_id != mItem->getParentUUID();
+
+ LLPointer<LLViewerInventoryItem> new_item = new LLViewerInventoryItem(mItem);
+
+ if (!current_title_value.empty() &&
+ (item_title_value != current_title_value || item_notes_value != current_notes_value))
+ {
+ new_item->rename(current_title_value);
+ new_item->setDescription(current_notes_value);
+ LLPointer<LLInventoryCallback> cb;
+ if (change_parent)
+ {
+ cb = new LLUpdateLandmarkParent(new_item, folder_id);
+ }
+ LLInventoryModel::LLCategoryUpdate up(mItem->getParentUUID(), 0);
+ gInventory.accountForUpdate(up);
+ update_inventory_item(new_item, cb);
+ }
+ else if (change_parent)
+ {
+ LLInventoryModel::update_list_t update;
+ LLInventoryModel::LLCategoryUpdate old_folder(mItem->getParentUUID(),-1);
+ update.push_back(old_folder);
+ LLInventoryModel::LLCategoryUpdate new_folder(folder_id, 1);
+ update.push_back(new_folder);
+ gInventory.accountForUpdate(update);
+
+ new_item->setParent(folder_id);
+ new_item->updateParentOnServer(false);
+ }
+
+ gInventory.updateItem(new_item);
+ gInventory.notifyObservers();
+
+ onCancelButtonClicked();
+}
+
+void LLPanelPlaces::onCancelButtonClicked()
+{
+ if (!mLandmarkInfo)
+ return;
+
+ if (mPlaceInfoType == CREATE_LANDMARK_INFO_TYPE)
+ {
+ onBackButtonClicked();
+ }
+ else
+ {
+ mLandmarkInfo->toggleLandmarkEditMode(false);
+ isLandmarkEditModeOn = false;
+
+ updateVerbs();
+
+ // Reload the landmark properties.
+ mLandmarkInfo->displayItemInfo(mItem);
+ }
+}
+
+void LLPanelPlaces::onOverflowButtonClicked()
+{
+ LLToggleableMenu* menu;
+
+ bool is_agent_place_info_visible = mPlaceInfoType == AGENT_INFO_TYPE;
+
+ if ((is_agent_place_info_visible ||
+ mPlaceInfoType == REMOTE_PLACE_INFO_TYPE ||
+ mPlaceInfoType == TELEPORT_HISTORY_INFO_TYPE) && mPlaceMenu != NULL)
+ {
+ menu = mPlaceMenu;
+
+ bool landmark_item_enabled = false;
+ LLViewerParcelMgr* parcel_mgr = LLViewerParcelMgr::getInstance();
+ if (is_agent_place_info_visible
+ && gAgent.getRegion()
+ && mRegionId == gAgent.getRegion()->getRegionID()
+ && parcel_mgr
+ && parcel_mgr->getAgentParcel()->getLocalID() == mParcelLocalId)
+ {
+ // Floater still shows location identical to agent's position
+ landmark_item_enabled = !LLLandmarkActions::landmarkAlreadyExists();
+ }
+
+ // Enable adding a landmark only for agent current parcel and if
+ // there is no landmark already pointing to that parcel in agent's inventory.
+ menu->getChild<LLMenuItemCallGL>("landmark")->setEnabled(landmark_item_enabled);
+ // STORM-411
+ // Creating landmarks for remote locations is impossible.
+ // So hide menu item "Make a Landmark" in "Teleport History Profile" panel.
+ menu->setItemVisible("landmark", mPlaceInfoType != TELEPORT_HISTORY_INFO_TYPE);
+ menu->arrangeAndClear();
+ }
+ else if (mPlaceInfoType == LANDMARK_INFO_TYPE && mLandmarkMenu != NULL)
+ {
+ menu = mLandmarkMenu;
+
+ bool is_landmark_removable = false;
+ if (mItem.notNull())
+ {
+ const LLUUID& item_id = mItem->getUUID();
+ const LLUUID trash_id = gInventory.findCategoryUUIDForType(LLFolderType::FT_TRASH);
+ is_landmark_removable = gInventory.isObjectDescendentOf(item_id, gInventory.getRootFolderID()) &&
+ !gInventory.isObjectDescendentOf(item_id, trash_id);
+ }
+
+ menu->getChild<LLMenuItemCallGL>("delete")->setEnabled(is_landmark_removable);
+ }
+ else
+ {
+ return;
+ }
+
+ mOverflowBtn->setMenu(menu, LLMenuButton::MP_TOP_RIGHT);
+}
+
+bool LLPanelPlaces::onOverflowMenuItemEnable(const LLSD& param)
+{
+ std::string value = param.asString();
+ if("can_create_pick" == value)
+ {
+ return !LLAgentPicksInfo::getInstance()->isPickLimitReached();
+ }
+ return true;
+}
+
+void LLPanelPlaces::onOverflowMenuItemClicked(const LLSD& param)
+{
+ std::string item = param.asString();
+ if (item == "landmark")
+ {
+ LLSD key;
+ key["type"] = CREATE_LANDMARK_INFO_TYPE;
+ key["x"] = mPosGlobal.mdV[VX];
+ key["y"] = mPosGlobal.mdV[VY];
+ key["z"] = mPosGlobal.mdV[VZ];
+ onOpen(key);
+ }
+ else if (item == "copy")
+ {
+ LLLandmarkActions::getSLURLfromPosGlobal(mPosGlobal, boost::bind(&onSLURLBuilt, _1));
+ }
+ else if (item == "delete")
+ {
+ gInventory.removeItem(mItem->getUUID());
+
+ onBackButtonClicked();
+ }
+ else if (item == "pick")
+ {
+ LLPanelPlaceInfo* panel = getCurrentInfoPanel();
+ if (panel)
+ {
+ panel->createPick(mPosGlobal);
+ }
+ }
+ else if (item == "add_to_favbar")
+ {
+ if ( mItem.notNull() )
+ {
+ const LLUUID& favorites_id = gInventory.findCategoryUUIDForType(LLFolderType::FT_FAVORITE);
+ if ( favorites_id.notNull() )
+ {
+ copy_inventory_item(gAgent.getID(),
+ mItem->getPermissions().getOwner(),
+ mItem->getUUID(),
+ favorites_id,
+ std::string(),
+ LLPointer<LLInventoryCallback>(NULL));
+ LL_INFOS() << "Copied inventory item #" << mItem->getUUID() << " to favorites." << LL_ENDL;
+ }
+ }
+ }
+}
+
+void LLPanelPlaces::onBackButtonClicked()
+{
+ togglePlaceInfoPanel(false);
+
+ // Resetting mPlaceInfoType when Place Info panel is closed.
+ mPlaceInfoType = LLStringUtil::null;
+
+ isLandmarkEditModeOn = false;
+
+ updateVerbs();
+}
+
+void LLPanelPlaces::onGearMenuClick()
+{
+ if (mActivePanel)
+ {
+ LLToggleableMenu* menu = mActivePanel->getSelectionMenu();
+ mGearMenuButton->setMenu(menu, LLMenuButton::MP_BOTTOM_LEFT);
+ }
+}
+
+void LLPanelPlaces::onSortingMenuClick()
+{
+ if (mActivePanel)
+ {
+ LLToggleableMenu* menu = mActivePanel->getSortingMenu();
+ mSortingMenuButton->setMenu(menu, LLMenuButton::MP_BOTTOM_LEFT);
+ }
+}
+
+void LLPanelPlaces::onAddMenuClick()
+{
+ if (mActivePanel)
+ {
+ LLToggleableMenu* menu = mActivePanel->getCreateMenu();
+ mAddMenuButton->setMenu(menu, LLMenuButton::MP_BOTTOM_LEFT);
+ }
+}
+
+void LLPanelPlaces::onRemoveButtonClicked()
+{
+ if (mActivePanel)
+ {
+ mActivePanel->onRemoveSelected();
+ }
+}
+
+bool LLPanelPlaces::handleDragAndDropToTrash(bool drop, EDragAndDropType cargo_type, void* cargo_data, EAcceptance* accept)
+{
+ if (mActivePanel)
+ {
+ return mActivePanel->handleDragAndDropToTrash(drop, cargo_type, cargo_data, accept);
+ }
+ return false;
+}
+
+void LLPanelPlaces::togglePlaceInfoPanel(bool visible)
+{
+ if (!mPlaceProfile || !mLandmarkInfo)
+ return;
+
+ mTabContainer->setVisible(!visible);
+ mButtonsContainer->setVisible(visible);
+ mFilterContainer->setVisible(!visible);
+
+ if (mPlaceInfoType == AGENT_INFO_TYPE ||
+ mPlaceInfoType == REMOTE_PLACE_INFO_TYPE ||
+ mPlaceInfoType == TELEPORT_HISTORY_INFO_TYPE)
+ {
+ mPlaceProfile->setVisible(visible);
+
+ if (visible)
+ {
+ mPlaceProfile->resetLocation();
+
+ // Do not reset location info until mResetInfoTimer has expired
+ // to avoid text blinking.
+ mResetInfoTimer.setTimerExpirySec(PLACE_INFO_UPDATE_INTERVAL);
+
+ mLandmarkInfo->setVisible(false);
+ }
+ else if (mPlaceInfoType == AGENT_INFO_TYPE)
+ {
+ LLViewerParcelMgr::getInstance()->removeObserver(mParcelObserver);
+
+ // Clear reference to parcel selection when closing place profile panel.
+ // LLViewerParcelMgr removes the selection if it has 1 reference to it.
+ mParcel.clear();
+ }
+ }
+ else if (mPlaceInfoType == CREATE_LANDMARK_INFO_TYPE ||
+ mPlaceInfoType == LANDMARK_INFO_TYPE ||
+ mPlaceInfoType == LANDMARK_TAB_INFO_TYPE)
+ {
+ mLandmarkInfo->setVisible(visible);
+ mPlaceProfile->setVisible(false);
+ if (visible)
+ {
+ mLandmarkInfo->resetLocation();
+ }
+ else
+ {
+ std::string tab_panel_name("Landmarks");
+ if (mItem.notNull())
+ {
+ if (gInventory.isObjectDescendentOf(mItem->getUUID(), gInventory.findCategoryUUIDForType(LLFolderType::FT_FAVORITE)))
+ {
+ tab_panel_name = "Favorites";
+ }
+ }
+
+ LLLandmarksPanel* landmarks_panel = dynamic_cast<LLLandmarksPanel*>(mTabContainer->getPanelByName(tab_panel_name));
+ if (landmarks_panel)
+ {
+ // If a landmark info is being closed we open the landmarks tab
+ // and set this landmark selected.
+ mTabContainer->selectTabPanel(landmarks_panel);
+ if (mItem.notNull())
+ {
+ landmarks_panel->setItemSelected(mItem->getUUID(), true);
+ }
+ else
+ {
+ landmarks_panel->resetSelection();
+ }
+ }
+ }
+ }
+}
+
+// virtual
+void LLPanelPlaces::onVisibilityChange(bool new_visibility)
+{
+ LLPanel::onVisibilityChange(new_visibility);
+
+ if (!new_visibility && mPlaceInfoType == AGENT_INFO_TYPE)
+ {
+ LLViewerParcelMgr::getInstance()->removeObserver(mParcelObserver);
+
+ // Clear reference to parcel selection when closing places panel.
+ mParcel.clear();
+ }
+}
+
+void LLPanelPlaces::changedParcelSelection()
+{
+ if (!mPlaceProfile)
+ return;
+
+ LLViewerParcelMgr* parcel_mgr = LLViewerParcelMgr::getInstance();
+ mParcel = parcel_mgr->getFloatingParcelSelection();
+ LLParcel* parcel = mParcel->getParcel();
+ LLViewerRegion* region = parcel_mgr->getSelectionRegion();
+ if (!region || !parcel)
+ return;
+
+ LLVector3d prev_pos_global = mPosGlobal;
+
+ // If agent is inside the selected parcel show agent's region<X, Y, Z>,
+ // otherwise show region<X, Y, Z> of agent's selection point.
+ bool is_current_parcel = is_agent_in_selected_parcel(parcel);
+ if (is_current_parcel)
+ {
+ mPosGlobal = gAgent.getPositionGlobal();
+ }
+ else
+ {
+ LLVector3d pos_global = gViewerWindow->getLastPick().mPosGlobal;
+ if (!pos_global.isExactlyZero())
+ {
+ mPosGlobal = pos_global;
+ }
+ }
+
+ // Reset location info only if global position has changed
+ // and update timer has expired to reduce unnecessary text and icons updates.
+ if (prev_pos_global != mPosGlobal && mResetInfoTimer.hasExpired())
+ {
+ mPlaceProfile->resetLocation();
+ mResetInfoTimer.setTimerExpirySec(PLACE_INFO_UPDATE_INTERVAL);
+ }
+
+ mPlaceProfile->displaySelectedParcelInfo(parcel, region, mPosGlobal, is_current_parcel);
+
+ updateVerbs();
+}
+
+void LLPanelPlaces::createTabs()
+{
+ if (!(gInventory.isInventoryUsable() && LLTeleportHistory::getInstance() && !mTabsCreated))
+ return;
+
+ LLFavoritesPanel* favorites_panel = new LLFavoritesPanel();
+ if (favorites_panel)
+ {
+ mTabContainer->addTabPanel(
+ LLTabContainer::TabPanelParams().
+ panel(favorites_panel).
+ label(getString("favorites_tab_title")).
+ insert_at(LLTabContainer::END));
+ }
+
+ LLLandmarksPanel* landmarks_panel = new LLLandmarksPanel();
+ if (landmarks_panel)
+ {
+ mTabContainer->addTabPanel(
+ LLTabContainer::TabPanelParams().
+ panel(landmarks_panel).
+ label(getString("landmarks_tab_title")).
+ insert_at(LLTabContainer::END));
+ }
+
+ LLTeleportHistoryPanel* teleport_history_panel = new LLTeleportHistoryPanel();
+ if (teleport_history_panel)
+ {
+ mTabContainer->addTabPanel(
+ LLTabContainer::TabPanelParams().
+ panel(teleport_history_panel).
+ label(getString("teleport_history_tab_title")).
+ insert_at(LLTabContainer::END));
+ }
+
+ mTabContainer->selectFirstTab();
+
+ mActivePanel = dynamic_cast<LLPanelPlacesTab*>(mTabContainer->getCurrentPanel());
+
+ if (mActivePanel)
+ {
+ // Filter applied to show all items.
+ mActivePanel->onSearchEdit(mActivePanel->getFilterSubString());
+
+ // History panel does not support deletion nor creation
+ // Hide menus
+ bool supports_create = mActivePanel->getCreateMenu() != NULL;
+ childSetVisible("add_btn_panel", supports_create);
+
+ // favorites and inventory can remove items, history can clear history
+ childSetVisible("trash_btn_panel", true);
+
+ if (supports_create)
+ {
+ mRemoveSelectedBtn->setToolTip(getString("tooltip_trash_items"));
+ }
+ else
+ {
+ mRemoveSelectedBtn->setToolTip(getString("tooltip_trash_history"));
+ }
+
+ mActivePanel->setRemoveBtn(mRemoveSelectedBtn);
+ mActivePanel->updateVerbs();
+ }
+
+ mTabsCreated = true;
+}
+
+void LLPanelPlaces::changedGlobalPos(const LLVector3d &global_pos)
+{
+ mPosGlobal = global_pos;
+ updateVerbs();
+}
+
+void LLPanelPlaces::showAddedLandmarkInfo(const uuid_set_t& items)
+{
+ for (uuid_set_t::const_iterator item_iter = items.begin();
+ item_iter != items.end();
+ ++item_iter)
+ {
+ const LLUUID& item_id = (*item_iter);
+ if(!highlight_offered_object(item_id))
+ {
+ continue;
+ }
+
+ LLInventoryItem* item = gInventory.getItem(item_id);
+
+ llassert(item);
+ if (item && (LLAssetType::AT_LANDMARK == item->getType()) )
+ {
+ // Created landmark is passed to Places panel to allow its editing.
+ // If the panel is closed we don't reopen it until created landmark is loaded.
+ if("create_landmark" == getPlaceInfoType() && !getItem())
+ {
+ setItem(item);
+ }
+ }
+ }
+}
+
+void LLPanelPlaces::updateVerbs()
+{
+ bool is_place_info_visible;
+
+ LLPanelPlaceInfo* panel = getCurrentInfoPanel();
+ if (panel)
+ {
+ is_place_info_visible = panel->getVisible();
+ }
+ else
+ {
+ is_place_info_visible = false;
+ }
+
+ bool is_agent_place_info_visible = mPlaceInfoType == AGENT_INFO_TYPE;
+ bool is_create_landmark_visible = mPlaceInfoType == CREATE_LANDMARK_INFO_TYPE;
+
+ bool have_3d_pos = ! mPosGlobal.isExactlyZero();
+
+ mTeleportBtn->setVisible(!is_create_landmark_visible && !isLandmarkEditModeOn);
+ mShowOnMapBtn->setVisible(!is_create_landmark_visible && !isLandmarkEditModeOn);
+ mSaveBtn->setVisible(isLandmarkEditModeOn);
+ mCancelBtn->setVisible(isLandmarkEditModeOn);
+ mCloseBtn->setVisible(is_create_landmark_visible && !isLandmarkEditModeOn);
+
+ bool show_options_btn = is_place_info_visible && !is_create_landmark_visible && !isLandmarkEditModeOn;
+ mOverflowBtn->setVisible(show_options_btn);
+ getChild<LLLayoutPanel>("lp_options")->setVisible(show_options_btn);
+ getChild<LLLayoutPanel>("lp2")->setVisible(!show_options_btn);
+
+ if (is_place_info_visible)
+ {
+ mShowOnMapBtn->setEnabled(have_3d_pos);
+
+ if (is_agent_place_info_visible)
+ {
+ // We don't need to teleport to the current location
+ // so check if the location is not within the current parcel.
+ mTeleportBtn->setEnabled(have_3d_pos &&
+ !LLViewerParcelMgr::getInstance()->inAgentParcel(mPosGlobal));
+ }
+ else if (mPlaceInfoType == LANDMARK_INFO_TYPE || mPlaceInfoType == REMOTE_PLACE_INFO_TYPE)
+ {
+ mTeleportBtn->setEnabled(have_3d_pos);
+ }
+ }
+ else
+ {
+ if (mActivePanel)
+ mActivePanel->updateVerbs();
+ }
+}
+
+LLPanelPlaceInfo* LLPanelPlaces::getCurrentInfoPanel()
+{
+ if (mPlaceInfoType == AGENT_INFO_TYPE ||
+ mPlaceInfoType == REMOTE_PLACE_INFO_TYPE ||
+ mPlaceInfoType == TELEPORT_HISTORY_INFO_TYPE)
+ {
+ return mPlaceProfile;
+ }
+ else if (mPlaceInfoType == CREATE_LANDMARK_INFO_TYPE ||
+ mPlaceInfoType == LANDMARK_INFO_TYPE ||
+ mPlaceInfoType == LANDMARK_TAB_INFO_TYPE)
+ {
+ return mLandmarkInfo;
+ }
+
+ return NULL;
+}
+
+static bool is_agent_in_selected_parcel(LLParcel* parcel)
+{
+ LLViewerParcelMgr* parcel_mgr = LLViewerParcelMgr::getInstance();
+
+ LLViewerRegion* region = parcel_mgr->getSelectionRegion();
+ if (!region || !parcel)
+ return false;
+
+ return region == gAgent.getRegion() &&
+ parcel->getLocalID() == parcel_mgr->getAgentParcel()->getLocalID();
+}
+
+static void onSLURLBuilt(std::string& slurl)
+{
+ LLView::getWindow()->copyTextToClipboard(utf8str_to_wstring(slurl));
+
+ LLSD args;
+ args["SLURL"] = slurl;
+
+ LLNotificationsUtil::add("CopySLURL", args);
+}