/indra/newview/llpanelplaces.cpp

https://bitbucket.org/lindenlab/viewer-beta/ · C++ · 1220 lines · 948 code · 192 blank · 80 comment · 232 complexity · d6eeff1d55925855d6ee9942d47afb98 MD5 · raw file

  1. /**
  2. * @file llpanelplaces.cpp
  3. * @brief Side Bar "Places" panel
  4. *
  5. * $LicenseInfo:firstyear=2009&license=viewerlgpl$
  6. * Second Life Viewer Source Code
  7. * Copyright (C) 2010, Linden Research, Inc.
  8. *
  9. * This library is free software; you can redistribute it and/or
  10. * modify it under the terms of the GNU Lesser General Public
  11. * License as published by the Free Software Foundation;
  12. * version 2.1 of the License only.
  13. *
  14. * This library is distributed in the hope that it will be useful,
  15. * but WITHOUT ANY WARRANTY; without even the implied warranty of
  16. * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
  17. * Lesser General Public License for more details.
  18. *
  19. * You should have received a copy of the GNU Lesser General Public
  20. * License along with this library; if not, write to the Free Software
  21. * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA
  22. *
  23. * Linden Research, Inc., 945 Battery Street, San Francisco, CA 94111 USA
  24. * $/LicenseInfo$
  25. */
  26. #include "llviewerprecompiledheaders.h"
  27. #include "llpanelplaces.h"
  28. #include "llassettype.h"
  29. #include "lltimer.h"
  30. #include "llinventory.h"
  31. #include "lllandmark.h"
  32. #include "llparcel.h"
  33. #include "llcombobox.h"
  34. #include "llfiltereditor.h"
  35. #include "llfirstuse.h"
  36. #include "llfloaterreg.h"
  37. #include "llfloatersidepanelcontainer.h"
  38. #include "llmenubutton.h"
  39. #include "llnotificationsutil.h"
  40. #include "lltabcontainer.h"
  41. #include "lltexteditor.h"
  42. #include "lltrans.h"
  43. #include "lluictrlfactory.h"
  44. #include "llwindow.h"
  45. #include "llagent.h"
  46. #include "llagentpicksinfo.h"
  47. #include "llavatarpropertiesprocessor.h"
  48. #include "llcommandhandler.h"
  49. #include "llfloaterworldmap.h"
  50. #include "llinventorybridge.h"
  51. #include "llinventoryobserver.h"
  52. #include "llinventorymodel.h"
  53. #include "lllandmarkactions.h"
  54. #include "lllandmarklist.h"
  55. #include "llpanellandmarkinfo.h"
  56. #include "llpanellandmarks.h"
  57. #include "llpanelpick.h"
  58. #include "llpanelplaceprofile.h"
  59. #include "llpanelteleporthistory.h"
  60. #include "llremoteparcelrequest.h"
  61. #include "llteleporthistorystorage.h"
  62. #include "lltoggleablemenu.h"
  63. #include "llviewerinventory.h"
  64. #include "llviewermenu.h"
  65. #include "llviewermessage.h"
  66. #include "llviewerparcelmgr.h"
  67. #include "llviewerregion.h"
  68. #include "llviewerwindow.h"
  69. // Constants
  70. static const S32 LANDMARK_FOLDERS_MENU_WIDTH = 250;
  71. static const F32 PLACE_INFO_UPDATE_INTERVAL = 3.0;
  72. static const std::string AGENT_INFO_TYPE = "agent";
  73. static const std::string CREATE_LANDMARK_INFO_TYPE = "create_landmark";
  74. static const std::string LANDMARK_INFO_TYPE = "landmark";
  75. static const std::string REMOTE_PLACE_INFO_TYPE = "remote_place";
  76. static const std::string TELEPORT_HISTORY_INFO_TYPE = "teleport_history";
  77. static const std::string LANDMARK_TAB_INFO_TYPE = "open_landmark_tab";
  78. // Support for secondlife:///app/parcel/{UUID}/about SLapps
  79. class LLParcelHandler : public LLCommandHandler
  80. {
  81. public:
  82. // requires trusted browser to trigger
  83. LLParcelHandler() : LLCommandHandler("parcel", UNTRUSTED_THROTTLE) { }
  84. bool handle(const LLSD& params, const LLSD& query_map,
  85. LLMediaCtrl* web)
  86. {
  87. if (params.size() < 2)
  88. {
  89. return false;
  90. }
  91. if (!LLUI::sSettingGroups["config"]->getBOOL("EnablePlaceProfile"))
  92. {
  93. LLNotificationsUtil::add("NoPlaceInfo", LLSD(), LLSD(), std::string("SwitchToStandardSkinAndQuit"));
  94. return true;
  95. }
  96. LLUUID parcel_id;
  97. if (!parcel_id.set(params[0], FALSE))
  98. {
  99. return false;
  100. }
  101. if (params[1].asString() == "about")
  102. {
  103. if (parcel_id.notNull())
  104. {
  105. LLSD key;
  106. key["type"] = "remote_place";
  107. key["id"] = parcel_id;
  108. LLFloaterSidePanelContainer::showPanel("places", key);
  109. return true;
  110. }
  111. }
  112. return false;
  113. }
  114. };
  115. LLParcelHandler gParcelHandler;
  116. // Helper functions
  117. static bool is_agent_in_selected_parcel(LLParcel* parcel);
  118. static void onSLURLBuilt(std::string& slurl);
  119. //Observer classes
  120. class LLPlacesParcelObserver : public LLParcelObserver
  121. {
  122. public:
  123. LLPlacesParcelObserver(LLPanelPlaces* places_panel) :
  124. LLParcelObserver(),
  125. mPlaces(places_panel)
  126. {}
  127. /*virtual*/ void changed()
  128. {
  129. if (mPlaces)
  130. mPlaces->changedParcelSelection();
  131. }
  132. private:
  133. LLPanelPlaces* mPlaces;
  134. };
  135. class LLPlacesInventoryObserver : public LLInventoryAddedObserver
  136. {
  137. public:
  138. LLPlacesInventoryObserver(LLPanelPlaces* places_panel) :
  139. mPlaces(places_panel)
  140. {}
  141. /*virtual*/ void changed(U32 mask)
  142. {
  143. LLInventoryAddedObserver::changed(mask);
  144. if (mPlaces && !mPlaces->tabsCreated())
  145. {
  146. mPlaces->createTabs();
  147. }
  148. }
  149. protected:
  150. /*virtual*/ void done()
  151. {
  152. mPlaces->showAddedLandmarkInfo(mAdded);
  153. mAdded.clear();
  154. }
  155. private:
  156. LLPanelPlaces* mPlaces;
  157. };
  158. class LLPlacesRemoteParcelInfoObserver : public LLRemoteParcelInfoObserver
  159. {
  160. public:
  161. LLPlacesRemoteParcelInfoObserver(LLPanelPlaces* places_panel) :
  162. LLRemoteParcelInfoObserver(),
  163. mPlaces(places_panel)
  164. {}
  165. ~LLPlacesRemoteParcelInfoObserver()
  166. {
  167. // remove any in-flight observers
  168. std::set<LLUUID>::iterator it;
  169. for (it = mParcelIDs.begin(); it != mParcelIDs.end(); ++it)
  170. {
  171. const LLUUID &id = *it;
  172. LLRemoteParcelInfoProcessor::getInstance()->removeObserver(id, this);
  173. }
  174. mParcelIDs.clear();
  175. }
  176. /*virtual*/ void processParcelInfo(const LLParcelData& parcel_data)
  177. {
  178. if (mPlaces)
  179. {
  180. mPlaces->changedGlobalPos(LLVector3d(parcel_data.global_x,
  181. parcel_data.global_y,
  182. parcel_data.global_z));
  183. }
  184. mParcelIDs.erase(parcel_data.parcel_id);
  185. LLRemoteParcelInfoProcessor::getInstance()->removeObserver(parcel_data.parcel_id, this);
  186. }
  187. /*virtual*/ void setParcelID(const LLUUID& parcel_id)
  188. {
  189. if (!parcel_id.isNull())
  190. {
  191. mParcelIDs.insert(parcel_id);
  192. LLRemoteParcelInfoProcessor::getInstance()->addObserver(parcel_id, this);
  193. LLRemoteParcelInfoProcessor::getInstance()->sendParcelInfoRequest(parcel_id);
  194. }
  195. }
  196. /*virtual*/ void setErrorStatus(U32 status, const std::string& reason)
  197. {
  198. llerrs << "Can't complete remote parcel request. Http Status: "
  199. << status << ". Reason : " << reason << llendl;
  200. }
  201. private:
  202. std::set<LLUUID> mParcelIDs;
  203. LLPanelPlaces* mPlaces;
  204. };
  205. static LLRegisterPanelClassWrapper<LLPanelPlaces> t_places("panel_places");
  206. LLPanelPlaces::LLPanelPlaces()
  207. : LLPanel(),
  208. mActivePanel(NULL),
  209. mFilterEditor(NULL),
  210. mPlaceProfile(NULL),
  211. mLandmarkInfo(NULL),
  212. mPickPanel(NULL),
  213. mItem(NULL),
  214. mPlaceMenu(NULL),
  215. mLandmarkMenu(NULL),
  216. mPosGlobal(),
  217. isLandmarkEditModeOn(false),
  218. mTabsCreated(false)
  219. {
  220. mParcelObserver = new LLPlacesParcelObserver(this);
  221. mInventoryObserver = new LLPlacesInventoryObserver(this);
  222. mRemoteParcelObserver = new LLPlacesRemoteParcelInfoObserver(this);
  223. gInventory.addObserver(mInventoryObserver);
  224. mAgentParcelChangedConnection = LLViewerParcelMgr::getInstance()->addAgentParcelChangedCallback(
  225. boost::bind(&LLPanelPlaces::updateVerbs, this));
  226. //buildFromFile( "panel_places.xml"); // Called from LLRegisterPanelClass::defaultPanelClassBuilder()
  227. }
  228. LLPanelPlaces::~LLPanelPlaces()
  229. {
  230. if (gInventory.containsObserver(mInventoryObserver))
  231. gInventory.removeObserver(mInventoryObserver);
  232. LLViewerParcelMgr::getInstance()->removeObserver(mParcelObserver);
  233. delete mInventoryObserver;
  234. delete mParcelObserver;
  235. delete mRemoteParcelObserver;
  236. if (mAgentParcelChangedConnection.connected())
  237. {
  238. mAgentParcelChangedConnection.disconnect();
  239. }
  240. }
  241. BOOL LLPanelPlaces::postBuild()
  242. {
  243. mTeleportBtn = getChild<LLButton>("teleport_btn");
  244. mTeleportBtn->setClickedCallback(boost::bind(&LLPanelPlaces::onTeleportButtonClicked, this));
  245. mShowOnMapBtn = getChild<LLButton>("map_btn");
  246. mShowOnMapBtn->setClickedCallback(boost::bind(&LLPanelPlaces::onShowOnMapButtonClicked, this));
  247. mEditBtn = getChild<LLButton>("edit_btn");
  248. mEditBtn->setClickedCallback(boost::bind(&LLPanelPlaces::onEditButtonClicked, this));
  249. mSaveBtn = getChild<LLButton>("save_btn");
  250. mSaveBtn->setClickedCallback(boost::bind(&LLPanelPlaces::onSaveButtonClicked, this));
  251. mCancelBtn = getChild<LLButton>("cancel_btn");
  252. mCancelBtn->setClickedCallback(boost::bind(&LLPanelPlaces::onCancelButtonClicked, this));
  253. mCloseBtn = getChild<LLButton>("close_btn");
  254. mCloseBtn->setClickedCallback(boost::bind(&LLPanelPlaces::onBackButtonClicked, this));
  255. mOverflowBtn = getChild<LLMenuButton>("overflow_btn");
  256. mOverflowBtn->setMouseDownCallback(boost::bind(&LLPanelPlaces::onOverflowButtonClicked, this));
  257. mPlaceInfoBtn = getChild<LLButton>("profile_btn");
  258. mPlaceInfoBtn->setClickedCallback(boost::bind(&LLPanelPlaces::onProfileButtonClicked, this));
  259. LLUICtrl::CommitCallbackRegistry::ScopedRegistrar registrar;
  260. registrar.add("Places.OverflowMenu.Action", boost::bind(&LLPanelPlaces::onOverflowMenuItemClicked, this, _2));
  261. LLUICtrl::EnableCallbackRegistry::ScopedRegistrar enable_registrar;
  262. enable_registrar.add("Places.OverflowMenu.Enable", boost::bind(&LLPanelPlaces::onOverflowMenuItemEnable, this, _2));
  263. mPlaceMenu = LLUICtrlFactory::getInstance()->createFromFile<LLToggleableMenu>("menu_place.xml", gMenuHolder, LLViewerMenuHolderGL::child_registry_t::instance());
  264. if (!mPlaceMenu)
  265. {
  266. llwarns << "Error loading Place menu" << llendl;
  267. }
  268. mLandmarkMenu = LLUICtrlFactory::getInstance()->createFromFile<LLToggleableMenu>("menu_landmark.xml", gMenuHolder, LLViewerMenuHolderGL::child_registry_t::instance());
  269. if (!mLandmarkMenu)
  270. {
  271. llwarns << "Error loading Landmark menu" << llendl;
  272. }
  273. mTabContainer = getChild<LLTabContainer>("Places Tabs");
  274. if (mTabContainer)
  275. {
  276. mTabContainer->setCommitCallback(boost::bind(&LLPanelPlaces::onTabSelected, this));
  277. }
  278. mFilterEditor = getChild<LLFilterEditor>("Filter");
  279. if (mFilterEditor)
  280. {
  281. //when list item is being clicked the filter editor looses focus
  282. //committing on focus lost leads to detaching list items
  283. //BUT a detached list item cannot be made selected and must not be clicked onto
  284. mFilterEditor->setCommitOnFocusLost(false);
  285. mFilterEditor->setCommitCallback(boost::bind(&LLPanelPlaces::onFilterEdit, this, _2, false));
  286. }
  287. mPlaceProfile = findChild<LLPanelPlaceProfile>("panel_place_profile");
  288. mLandmarkInfo = findChild<LLPanelLandmarkInfo>("panel_landmark_info");
  289. if (!mPlaceProfile || !mLandmarkInfo)
  290. return FALSE;
  291. mPlaceProfileBackBtn = mPlaceProfile->getChild<LLButton>("back_btn");
  292. mPlaceProfileBackBtn->setClickedCallback(boost::bind(&LLPanelPlaces::onBackButtonClicked, this));
  293. mLandmarkInfo->getChild<LLButton>("back_btn")->setClickedCallback(boost::bind(&LLPanelPlaces::onBackButtonClicked, this));
  294. LLLineEditor* title_editor = mLandmarkInfo->getChild<LLLineEditor>("title_editor");
  295. title_editor->setKeystrokeCallback(boost::bind(&LLPanelPlaces::onEditButtonClicked, this), NULL);
  296. LLTextEditor* notes_editor = mLandmarkInfo->getChild<LLTextEditor>("notes_editor");
  297. notes_editor->setKeystrokeCallback(boost::bind(&LLPanelPlaces::onEditButtonClicked, this));
  298. LLComboBox* folder_combo = mLandmarkInfo->getChild<LLComboBox>("folder_combo");
  299. folder_combo->setCommitCallback(boost::bind(&LLPanelPlaces::onEditButtonClicked, this));
  300. createTabs();
  301. updateVerbs();
  302. return TRUE;
  303. }
  304. void LLPanelPlaces::onOpen(const LLSD& key)
  305. {
  306. if (!mPlaceProfile || !mLandmarkInfo)
  307. return;
  308. if (key.size() != 0)
  309. {
  310. std::string key_type = key["type"].asString();
  311. if (key_type == LANDMARK_TAB_INFO_TYPE)
  312. {
  313. // Small hack: We need to toggle twice. The first toggle moves from the Landmark
  314. // or Teleport History info panel to the Landmark or Teleport History list panel.
  315. // For this first toggle, the mPlaceInfoType should be the one previously used so
  316. // that the state can be corretly set.
  317. // The second toggle forces the list to be set to Landmark.
  318. // This avoids extracting and duplicating all the state logic from togglePlaceInfoPanel()
  319. // here or some specific private method
  320. togglePlaceInfoPanel(FALSE);
  321. mPlaceInfoType = key_type;
  322. togglePlaceInfoPanel(FALSE);
  323. // Update the active tab
  324. onTabSelected();
  325. // Update the buttons at the bottom of the panel
  326. updateVerbs();
  327. }
  328. else
  329. {
  330. mFilterEditor->clear();
  331. onFilterEdit("", false);
  332. mPlaceInfoType = key_type;
  333. mPosGlobal.setZero();
  334. mItem = NULL;
  335. isLandmarkEditModeOn = false;
  336. togglePlaceInfoPanel(TRUE);
  337. if (mPlaceInfoType == AGENT_INFO_TYPE)
  338. {
  339. mPlaceProfile->setInfoType(LLPanelPlaceInfo::AGENT);
  340. }
  341. else if (mPlaceInfoType == CREATE_LANDMARK_INFO_TYPE)
  342. {
  343. mLandmarkInfo->setInfoType(LLPanelPlaceInfo::CREATE_LANDMARK);
  344. if (key.has("x") && key.has("y") && key.has("z"))
  345. {
  346. mPosGlobal = LLVector3d(key["x"].asReal(),
  347. key["y"].asReal(),
  348. key["z"].asReal());
  349. }
  350. else
  351. {
  352. mPosGlobal = gAgent.getPositionGlobal();
  353. }
  354. mLandmarkInfo->displayParcelInfo(LLUUID(), mPosGlobal);
  355. mSaveBtn->setEnabled(FALSE);
  356. }
  357. else if (mPlaceInfoType == LANDMARK_INFO_TYPE)
  358. {
  359. mLandmarkInfo->setInfoType(LLPanelPlaceInfo::LANDMARK);
  360. LLInventoryItem* item = gInventory.getItem(key["id"].asUUID());
  361. if (!item)
  362. return;
  363. setItem(item);
  364. }
  365. else if (mPlaceInfoType == REMOTE_PLACE_INFO_TYPE)
  366. {
  367. if (key.has("id"))
  368. {
  369. LLUUID parcel_id = key["id"].asUUID();
  370. mPlaceProfile->setParcelID(parcel_id);
  371. // query the server to get the global 3D position of this
  372. // parcel - we need this for teleport/mapping functions.
  373. mRemoteParcelObserver->setParcelID(parcel_id);
  374. }
  375. else
  376. {
  377. mPosGlobal = LLVector3d(key["x"].asReal(),
  378. key["y"].asReal(),
  379. key["z"].asReal());
  380. mPlaceProfile->displayParcelInfo(LLUUID(), mPosGlobal);
  381. }
  382. mPlaceProfile->setInfoType(LLPanelPlaceInfo::PLACE);
  383. }
  384. else if (mPlaceInfoType == TELEPORT_HISTORY_INFO_TYPE)
  385. {
  386. S32 index = key["id"].asInteger();
  387. const LLTeleportHistoryStorage::slurl_list_t& hist_items =
  388. LLTeleportHistoryStorage::getInstance()->getItems();
  389. mPosGlobal = hist_items[index].mGlobalPos;
  390. mPlaceProfile->setInfoType(LLPanelPlaceInfo::TELEPORT_HISTORY);
  391. mPlaceProfile->displayParcelInfo(LLUUID(), mPosGlobal);
  392. }
  393. updateVerbs();
  394. }
  395. }
  396. LLViewerParcelMgr* parcel_mgr = LLViewerParcelMgr::getInstance();
  397. if (!parcel_mgr)
  398. return;
  399. // Start using LLViewerParcelMgr for land selection if
  400. // information about nearby land is requested.
  401. // Otherwise stop using land selection and deselect land.
  402. if (mPlaceInfoType == AGENT_INFO_TYPE)
  403. {
  404. // We don't know if we are already added to LLViewerParcelMgr observers list
  405. // so try to remove observer not to add an extra one.
  406. parcel_mgr->removeObserver(mParcelObserver);
  407. parcel_mgr->addObserver(mParcelObserver);
  408. parcel_mgr->selectParcelAt(gAgent.getPositionGlobal());
  409. }
  410. else
  411. {
  412. parcel_mgr->removeObserver(mParcelObserver);
  413. // Clear the reference to selection to allow its removal in deselectUnused().
  414. mParcel.clear();
  415. if (!parcel_mgr->selectionEmpty())
  416. {
  417. parcel_mgr->deselectUnused();
  418. }
  419. }
  420. }
  421. void LLPanelPlaces::setItem(LLInventoryItem* item)
  422. {
  423. if (!mLandmarkInfo || !item)
  424. return;
  425. mItem = item;
  426. LLAssetType::EType item_type = mItem->getActualType();
  427. if (item_type == LLAssetType::AT_LANDMARK || item_type == LLAssetType::AT_LINK)
  428. {
  429. // If the item is a link get a linked item
  430. if (item_type == LLAssetType::AT_LINK)
  431. {
  432. mItem = gInventory.getItem(mItem->getLinkedUUID());
  433. if (mItem.isNull())
  434. return;
  435. }
  436. }
  437. else
  438. {
  439. return;
  440. }
  441. // Check if item is in agent's inventory and he has the permission to modify it.
  442. BOOL is_landmark_editable = gInventory.isObjectDescendentOf(mItem->getUUID(), gInventory.getRootFolderID()) &&
  443. mItem->getPermissions().allowModifyBy(gAgent.getID());
  444. mEditBtn->setEnabled(is_landmark_editable);
  445. mSaveBtn->setEnabled(is_landmark_editable);
  446. if (is_landmark_editable)
  447. {
  448. if(!mLandmarkInfo->setLandmarkFolder(mItem->getParentUUID()) && !mItem->getParentUUID().isNull())
  449. {
  450. const LLViewerInventoryCategory* cat = gInventory.getCategory(mItem->getParentUUID());
  451. if (cat)
  452. {
  453. std::string cat_fullname = LLPanelLandmarkInfo::getFullFolderName(cat);
  454. LLComboBox* folderList = mLandmarkInfo->getChild<LLComboBox>("folder_combo");
  455. folderList->add(cat_fullname, cat->getUUID(), ADD_TOP);
  456. }
  457. }
  458. }
  459. mLandmarkInfo->displayItemInfo(mItem);
  460. LLLandmark* lm = gLandmarkList.getAsset(mItem->getAssetUUID(),
  461. boost::bind(&LLPanelPlaces::onLandmarkLoaded, this, _1));
  462. if (lm)
  463. {
  464. onLandmarkLoaded(lm);
  465. }
  466. }
  467. S32 LLPanelPlaces::notifyParent(const LLSD& info)
  468. {
  469. if(info.has("update_verbs"))
  470. {
  471. if(mPosGlobal.isExactlyZero())
  472. {
  473. mPosGlobal.setVec(info["global_x"], info["global_y"], info["global_z"]);
  474. }
  475. updateVerbs();
  476. return 1;
  477. }
  478. return LLPanel::notifyParent(info);
  479. }
  480. void LLPanelPlaces::onLandmarkLoaded(LLLandmark* landmark)
  481. {
  482. if (!mLandmarkInfo)
  483. return;
  484. LLUUID region_id;
  485. landmark->getRegionID(region_id);
  486. landmark->getGlobalPos(mPosGlobal);
  487. mLandmarkInfo->displayParcelInfo(region_id, mPosGlobal);
  488. updateVerbs();
  489. }
  490. void LLPanelPlaces::onFilterEdit(const std::string& search_string, bool force_filter)
  491. {
  492. if (!mActivePanel)
  493. return;
  494. if (force_filter || mActivePanel->getFilterSubString() != search_string)
  495. {
  496. std::string string = search_string;
  497. // Searches are case-insensitive
  498. // but we don't convert the typed string to upper-case so that it can be fed to the web search as-is.
  499. mActivePanel->onSearchEdit(string);
  500. }
  501. }
  502. void LLPanelPlaces::onTabSelected()
  503. {
  504. mActivePanel = dynamic_cast<LLPanelPlacesTab*>(mTabContainer->getCurrentPanel());
  505. if (!mActivePanel)
  506. return;
  507. onFilterEdit(mActivePanel->getFilterSubString(), true);
  508. mActivePanel->updateVerbs();
  509. }
  510. void LLPanelPlaces::onTeleportButtonClicked()
  511. {
  512. LLPanelPlaceInfo* panel = getCurrentInfoPanel();
  513. if (panel && panel->getVisible())
  514. {
  515. if (mPlaceInfoType == LANDMARK_INFO_TYPE)
  516. {
  517. if (mItem.isNull())
  518. {
  519. llwarns << "NULL landmark item" << llendl;
  520. llassert(mItem.notNull());
  521. return;
  522. }
  523. LLSD payload;
  524. payload["asset_id"] = mItem->getAssetUUID();
  525. LLSD args;
  526. args["LOCATION"] = mItem->getName();
  527. LLNotificationsUtil::add("TeleportFromLandmark", args, payload);
  528. }
  529. else if (mPlaceInfoType == AGENT_INFO_TYPE ||
  530. mPlaceInfoType == REMOTE_PLACE_INFO_TYPE ||
  531. mPlaceInfoType == TELEPORT_HISTORY_INFO_TYPE)
  532. {
  533. LLFloaterWorldMap* worldmap_instance = LLFloaterWorldMap::getInstance();
  534. if (!mPosGlobal.isExactlyZero() && worldmap_instance)
  535. {
  536. gAgent.teleportViaLocation(mPosGlobal);
  537. worldmap_instance->trackLocation(mPosGlobal);
  538. }
  539. }
  540. }
  541. else
  542. {
  543. if (mActivePanel)
  544. mActivePanel->onTeleport();
  545. }
  546. }
  547. void LLPanelPlaces::onShowOnMapButtonClicked()
  548. {
  549. LLPanelPlaceInfo* panel = getCurrentInfoPanel();
  550. if (panel && panel->getVisible())
  551. {
  552. LLFloaterWorldMap* worldmap_instance = LLFloaterWorldMap::getInstance();
  553. if(!worldmap_instance)
  554. return;
  555. if (mPlaceInfoType == AGENT_INFO_TYPE ||
  556. mPlaceInfoType == CREATE_LANDMARK_INFO_TYPE ||
  557. mPlaceInfoType == REMOTE_PLACE_INFO_TYPE ||
  558. mPlaceInfoType == TELEPORT_HISTORY_INFO_TYPE)
  559. {
  560. if (!mPosGlobal.isExactlyZero())
  561. {
  562. worldmap_instance->trackLocation(mPosGlobal);
  563. LLFloaterReg::showInstance("world_map", "center");
  564. }
  565. }
  566. else if (mPlaceInfoType == LANDMARK_INFO_TYPE)
  567. {
  568. LLLandmark* landmark = gLandmarkList.getAsset(mItem->getAssetUUID());
  569. if (!landmark)
  570. return;
  571. LLVector3d landmark_global_pos;
  572. if (!landmark->getGlobalPos(landmark_global_pos))
  573. return;
  574. if (!landmark_global_pos.isExactlyZero())
  575. {
  576. worldmap_instance->trackLocation(landmark_global_pos);
  577. LLFloaterReg::showInstance("world_map", "center");
  578. }
  579. }
  580. }
  581. else
  582. {
  583. if (mActivePanel && mActivePanel->isSingleItemSelected())
  584. {
  585. mActivePanel->onShowOnMap();
  586. }
  587. else
  588. {
  589. LLFloaterWorldMap* worldmap_instance = LLFloaterWorldMap::getInstance();
  590. LLVector3d global_pos = gAgent.getPositionGlobal();
  591. if (!global_pos.isExactlyZero() && worldmap_instance)
  592. {
  593. worldmap_instance->trackLocation(global_pos);
  594. LLFloaterReg::showInstance("world_map", "center");
  595. }
  596. }
  597. }
  598. }
  599. void LLPanelPlaces::onEditButtonClicked()
  600. {
  601. if (!mLandmarkInfo || isLandmarkEditModeOn)
  602. return;
  603. isLandmarkEditModeOn = true;
  604. mLandmarkInfo->toggleLandmarkEditMode(TRUE);
  605. updateVerbs();
  606. }
  607. void LLPanelPlaces::onSaveButtonClicked()
  608. {
  609. if (!mLandmarkInfo || mItem.isNull())
  610. return;
  611. std::string current_title_value = mLandmarkInfo->getLandmarkTitle();
  612. std::string item_title_value = mItem->getName();
  613. std::string current_notes_value = mLandmarkInfo->getLandmarkNotes();
  614. std::string item_notes_value = mItem->getDescription();
  615. LLStringUtil::trim(current_title_value);
  616. LLStringUtil::trim(current_notes_value);
  617. LLUUID item_id = mItem->getUUID();
  618. LLUUID folder_id = mLandmarkInfo->getLandmarkFolder();
  619. LLPointer<LLViewerInventoryItem> new_item = new LLViewerInventoryItem(mItem);
  620. if (!current_title_value.empty() &&
  621. (item_title_value != current_title_value || item_notes_value != current_notes_value))
  622. {
  623. new_item->rename(current_title_value);
  624. new_item->setDescription(current_notes_value);
  625. new_item->updateServer(FALSE);
  626. }
  627. if(folder_id != mItem->getParentUUID())
  628. {
  629. LLInventoryModel::update_list_t update;
  630. LLInventoryModel::LLCategoryUpdate old_folder(mItem->getParentUUID(),-1);
  631. update.push_back(old_folder);
  632. LLInventoryModel::LLCategoryUpdate new_folder(folder_id, 1);
  633. update.push_back(new_folder);
  634. gInventory.accountForUpdate(update);
  635. new_item->setParent(folder_id);
  636. new_item->updateParentOnServer(FALSE);
  637. }
  638. gInventory.updateItem(new_item);
  639. gInventory.notifyObservers();
  640. onCancelButtonClicked();
  641. }
  642. void LLPanelPlaces::onCancelButtonClicked()
  643. {
  644. if (!mLandmarkInfo)
  645. return;
  646. if (mPlaceInfoType == CREATE_LANDMARK_INFO_TYPE)
  647. {
  648. onBackButtonClicked();
  649. }
  650. else
  651. {
  652. mLandmarkInfo->toggleLandmarkEditMode(FALSE);
  653. isLandmarkEditModeOn = false;
  654. updateVerbs();
  655. // Reload the landmark properties.
  656. mLandmarkInfo->displayItemInfo(mItem);
  657. }
  658. }
  659. void LLPanelPlaces::onOverflowButtonClicked()
  660. {
  661. LLToggleableMenu* menu;
  662. bool is_agent_place_info_visible = mPlaceInfoType == AGENT_INFO_TYPE;
  663. if ((is_agent_place_info_visible ||
  664. mPlaceInfoType == REMOTE_PLACE_INFO_TYPE ||
  665. mPlaceInfoType == TELEPORT_HISTORY_INFO_TYPE) && mPlaceMenu != NULL)
  666. {
  667. menu = mPlaceMenu;
  668. // Enable adding a landmark only for agent current parcel and if
  669. // there is no landmark already pointing to that parcel in agent's inventory.
  670. menu->getChild<LLMenuItemCallGL>("landmark")->setEnabled(is_agent_place_info_visible &&
  671. !LLLandmarkActions::landmarkAlreadyExists());
  672. // STORM-411
  673. // Creating landmarks for remote locations is impossible.
  674. // So hide menu item "Make a Landmark" in "Teleport History Profile" panel.
  675. menu->setItemVisible("landmark", mPlaceInfoType != TELEPORT_HISTORY_INFO_TYPE);
  676. menu->arrangeAndClear();
  677. }
  678. else if (mPlaceInfoType == LANDMARK_INFO_TYPE && mLandmarkMenu != NULL)
  679. {
  680. menu = mLandmarkMenu;
  681. BOOL is_landmark_removable = FALSE;
  682. if (mItem.notNull())
  683. {
  684. const LLUUID& item_id = mItem->getUUID();
  685. const LLUUID trash_id = gInventory.findCategoryUUIDForType(LLFolderType::FT_TRASH);
  686. is_landmark_removable = gInventory.isObjectDescendentOf(item_id, gInventory.getRootFolderID()) &&
  687. !gInventory.isObjectDescendentOf(item_id, trash_id);
  688. }
  689. menu->getChild<LLMenuItemCallGL>("delete")->setEnabled(is_landmark_removable);
  690. }
  691. else
  692. {
  693. return;
  694. }
  695. mOverflowBtn->setMenu(menu, LLMenuButton::MP_TOP_RIGHT);
  696. }
  697. void LLPanelPlaces::onProfileButtonClicked()
  698. {
  699. if (!mActivePanel)
  700. return;
  701. mActivePanel->onShowProfile();
  702. }
  703. bool LLPanelPlaces::onOverflowMenuItemEnable(const LLSD& param)
  704. {
  705. std::string value = param.asString();
  706. if("can_create_pick" == value)
  707. {
  708. return !LLAgentPicksInfo::getInstance()->isPickLimitReached();
  709. }
  710. return true;
  711. }
  712. void LLPanelPlaces::onOverflowMenuItemClicked(const LLSD& param)
  713. {
  714. std::string item = param.asString();
  715. if (item == "landmark")
  716. {
  717. LLSD key;
  718. key["type"] = CREATE_LANDMARK_INFO_TYPE;
  719. key["x"] = mPosGlobal.mdV[VX];
  720. key["y"] = mPosGlobal.mdV[VY];
  721. key["z"] = mPosGlobal.mdV[VZ];
  722. onOpen(key);
  723. }
  724. else if (item == "copy")
  725. {
  726. LLLandmarkActions::getSLURLfromPosGlobal(mPosGlobal, boost::bind(&onSLURLBuilt, _1));
  727. }
  728. else if (item == "delete")
  729. {
  730. gInventory.removeItem(mItem->getUUID());
  731. onBackButtonClicked();
  732. }
  733. else if (item == "pick")
  734. {
  735. if (mPickPanel == NULL)
  736. {
  737. mPickPanel = LLPanelPickEdit::create();
  738. addChild(mPickPanel);
  739. mPickPanel->setExitCallback(boost::bind(&LLPanelPlaces::togglePickPanel, this, FALSE));
  740. mPickPanel->setCancelCallback(boost::bind(&LLPanelPlaces::togglePickPanel, this, FALSE));
  741. mPickPanel->setSaveCallback(boost::bind(&LLPanelPlaces::togglePickPanel, this, FALSE));
  742. }
  743. togglePickPanel(TRUE);
  744. mPickPanel->onOpen(LLSD());
  745. LLPanelPlaceInfo* panel = getCurrentInfoPanel();
  746. if (panel)
  747. {
  748. panel->createPick(mPosGlobal, mPickPanel);
  749. }
  750. LLRect rect = getRect();
  751. mPickPanel->reshape(rect.getWidth(), rect.getHeight());
  752. mPickPanel->setRect(rect);
  753. }
  754. else if (item == "add_to_favbar")
  755. {
  756. if ( mItem.notNull() )
  757. {
  758. const LLUUID& favorites_id = gInventory.findCategoryUUIDForType(LLFolderType::FT_FAVORITE);
  759. if ( favorites_id.notNull() )
  760. {
  761. copy_inventory_item(gAgent.getID(),
  762. mItem->getPermissions().getOwner(),
  763. mItem->getUUID(),
  764. favorites_id,
  765. std::string(),
  766. LLPointer<LLInventoryCallback>(NULL));
  767. llinfos << "Copied inventory item #" << mItem->getUUID() << " to favorites." << llendl;
  768. }
  769. }
  770. }
  771. }
  772. void LLPanelPlaces::onBackButtonClicked()
  773. {
  774. togglePlaceInfoPanel(FALSE);
  775. // Resetting mPlaceInfoType when Place Info panel is closed.
  776. mPlaceInfoType = LLStringUtil::null;
  777. isLandmarkEditModeOn = false;
  778. updateVerbs();
  779. }
  780. void LLPanelPlaces::togglePickPanel(BOOL visible)
  781. {
  782. if (mPickPanel)
  783. mPickPanel->setVisible(visible);
  784. }
  785. void LLPanelPlaces::togglePlaceInfoPanel(BOOL visible)
  786. {
  787. if (!mPlaceProfile || !mLandmarkInfo)
  788. return;
  789. mFilterEditor->setVisible(!visible);
  790. mTabContainer->setVisible(!visible);
  791. if (mPlaceInfoType == AGENT_INFO_TYPE ||
  792. mPlaceInfoType == REMOTE_PLACE_INFO_TYPE ||
  793. mPlaceInfoType == TELEPORT_HISTORY_INFO_TYPE)
  794. {
  795. mPlaceProfile->setVisible(visible);
  796. if (visible)
  797. {
  798. mPlaceProfile->resetLocation();
  799. // Do not reset location info until mResetInfoTimer has expired
  800. // to avoid text blinking.
  801. mResetInfoTimer.setTimerExpirySec(PLACE_INFO_UPDATE_INTERVAL);
  802. LLRect rect = getRect();
  803. LLRect new_rect = LLRect(rect.mLeft, rect.mTop, rect.mRight, mTabContainer->getRect().mBottom);
  804. mPlaceProfile->reshape(new_rect.getWidth(), new_rect.getHeight());
  805. mLandmarkInfo->setVisible(FALSE);
  806. }
  807. else if (mPlaceInfoType == AGENT_INFO_TYPE)
  808. {
  809. LLViewerParcelMgr::getInstance()->removeObserver(mParcelObserver);
  810. // Clear reference to parcel selection when closing place profile panel.
  811. // LLViewerParcelMgr removes the selection if it has 1 reference to it.
  812. mParcel.clear();
  813. }
  814. }
  815. else if (mPlaceInfoType == CREATE_LANDMARK_INFO_TYPE ||
  816. mPlaceInfoType == LANDMARK_INFO_TYPE ||
  817. mPlaceInfoType == LANDMARK_TAB_INFO_TYPE)
  818. {
  819. mLandmarkInfo->setVisible(visible);
  820. if (visible)
  821. {
  822. mLandmarkInfo->resetLocation();
  823. LLRect rect = getRect();
  824. LLRect new_rect = LLRect(rect.mLeft, rect.mTop, rect.mRight, mTabContainer->getRect().mBottom);
  825. mLandmarkInfo->reshape(new_rect.getWidth(), new_rect.getHeight());
  826. mPlaceProfile->setVisible(FALSE);
  827. }
  828. else
  829. {
  830. LLLandmarksPanel* landmarks_panel =
  831. dynamic_cast<LLLandmarksPanel*>(mTabContainer->getPanelByName("Landmarks"));
  832. if (landmarks_panel)
  833. {
  834. // If a landmark info is being closed we open the landmarks tab
  835. // and set this landmark selected.
  836. mTabContainer->selectTabPanel(landmarks_panel);
  837. if (mItem.notNull())
  838. {
  839. landmarks_panel->setItemSelected(mItem->getUUID(), TRUE);
  840. }
  841. }
  842. }
  843. }
  844. }
  845. // virtual
  846. void LLPanelPlaces::handleVisibilityChange(BOOL new_visibility)
  847. {
  848. LLPanel::handleVisibilityChange(new_visibility);
  849. if (!new_visibility && mPlaceInfoType == AGENT_INFO_TYPE)
  850. {
  851. LLViewerParcelMgr::getInstance()->removeObserver(mParcelObserver);
  852. // Clear reference to parcel selection when closing places panel.
  853. mParcel.clear();
  854. }
  855. }
  856. void LLPanelPlaces::changedParcelSelection()
  857. {
  858. if (!mPlaceProfile)
  859. return;
  860. LLViewerParcelMgr* parcel_mgr = LLViewerParcelMgr::getInstance();
  861. mParcel = parcel_mgr->getFloatingParcelSelection();
  862. LLParcel* parcel = mParcel->getParcel();
  863. LLViewerRegion* region = parcel_mgr->getSelectionRegion();
  864. if (!region || !parcel)
  865. return;
  866. LLVector3d prev_pos_global = mPosGlobal;
  867. // If agent is inside the selected parcel show agent's region<X, Y, Z>,
  868. // otherwise show region<X, Y, Z> of agent's selection point.
  869. bool is_current_parcel = is_agent_in_selected_parcel(parcel);
  870. if (is_current_parcel)
  871. {
  872. mPosGlobal = gAgent.getPositionGlobal();
  873. }
  874. else
  875. {
  876. LLVector3d pos_global = gViewerWindow->getLastPick().mPosGlobal;
  877. if (!pos_global.isExactlyZero())
  878. {
  879. mPosGlobal = pos_global;
  880. }
  881. }
  882. // Reset location info only if global position has changed
  883. // and update timer has expired to reduce unnecessary text and icons updates.
  884. if (prev_pos_global != mPosGlobal && mResetInfoTimer.hasExpired())
  885. {
  886. mPlaceProfile->resetLocation();
  887. mResetInfoTimer.setTimerExpirySec(PLACE_INFO_UPDATE_INTERVAL);
  888. }
  889. mPlaceProfile->displaySelectedParcelInfo(parcel, region, mPosGlobal, is_current_parcel);
  890. updateVerbs();
  891. }
  892. void LLPanelPlaces::createTabs()
  893. {
  894. if (!(gInventory.isInventoryUsable() && LLTeleportHistory::getInstance() && !mTabsCreated))
  895. return;
  896. LLLandmarksPanel* landmarks_panel = new LLLandmarksPanel();
  897. if (landmarks_panel)
  898. {
  899. landmarks_panel->setPanelPlacesButtons(this);
  900. mTabContainer->addTabPanel(
  901. LLTabContainer::TabPanelParams().
  902. panel(landmarks_panel).
  903. label(getString("landmarks_tab_title")).
  904. insert_at(LLTabContainer::END));
  905. }
  906. LLTeleportHistoryPanel* teleport_history_panel = new LLTeleportHistoryPanel();
  907. if (teleport_history_panel)
  908. {
  909. teleport_history_panel->setPanelPlacesButtons(this);
  910. mTabContainer->addTabPanel(
  911. LLTabContainer::TabPanelParams().
  912. panel(teleport_history_panel).
  913. label(getString("teleport_history_tab_title")).
  914. insert_at(LLTabContainer::END));
  915. }
  916. mTabContainer->selectFirstTab();
  917. mActivePanel = dynamic_cast<LLPanelPlacesTab*>(mTabContainer->getCurrentPanel());
  918. // Filter applied to show all items.
  919. if (mActivePanel)
  920. mActivePanel->onSearchEdit(mActivePanel->getFilterSubString());
  921. mTabsCreated = true;
  922. }
  923. void LLPanelPlaces::changedGlobalPos(const LLVector3d &global_pos)
  924. {
  925. mPosGlobal = global_pos;
  926. updateVerbs();
  927. }
  928. void LLPanelPlaces::showAddedLandmarkInfo(const uuid_vec_t& items)
  929. {
  930. for (uuid_vec_t::const_iterator item_iter = items.begin();
  931. item_iter != items.end();
  932. ++item_iter)
  933. {
  934. const LLUUID& item_id = (*item_iter);
  935. if(!highlight_offered_object(item_id))
  936. {
  937. continue;
  938. }
  939. LLInventoryItem* item = gInventory.getItem(item_id);
  940. llassert(item);
  941. if (item && (LLAssetType::AT_LANDMARK == item->getType()) )
  942. {
  943. // Created landmark is passed to Places panel to allow its editing.
  944. // If the panel is closed we don't reopen it until created landmark is loaded.
  945. if("create_landmark" == getPlaceInfoType() && !getItem())
  946. {
  947. setItem(item);
  948. }
  949. }
  950. }
  951. }
  952. void LLPanelPlaces::updateVerbs()
  953. {
  954. bool is_place_info_visible;
  955. LLPanelPlaceInfo* panel = getCurrentInfoPanel();
  956. if (panel)
  957. {
  958. is_place_info_visible = panel->getVisible();
  959. }
  960. else
  961. {
  962. is_place_info_visible = false;
  963. }
  964. bool is_agent_place_info_visible = mPlaceInfoType == AGENT_INFO_TYPE;
  965. bool is_create_landmark_visible = mPlaceInfoType == CREATE_LANDMARK_INFO_TYPE;
  966. bool have_3d_pos = ! mPosGlobal.isExactlyZero();
  967. mTeleportBtn->setVisible(!is_create_landmark_visible && !isLandmarkEditModeOn);
  968. mShowOnMapBtn->setVisible(!is_create_landmark_visible && !isLandmarkEditModeOn);
  969. mOverflowBtn->setVisible(is_place_info_visible && !is_create_landmark_visible && !isLandmarkEditModeOn);
  970. mEditBtn->setVisible(mPlaceInfoType == LANDMARK_INFO_TYPE && !isLandmarkEditModeOn);
  971. mSaveBtn->setVisible(isLandmarkEditModeOn);
  972. mCancelBtn->setVisible(isLandmarkEditModeOn);
  973. mCloseBtn->setVisible(is_create_landmark_visible && !isLandmarkEditModeOn);
  974. mPlaceInfoBtn->setVisible(!is_place_info_visible && !is_create_landmark_visible && !isLandmarkEditModeOn);
  975. mPlaceInfoBtn->setEnabled(!is_create_landmark_visible && !isLandmarkEditModeOn && have_3d_pos);
  976. if (is_place_info_visible)
  977. {
  978. mShowOnMapBtn->setEnabled(have_3d_pos);
  979. if (is_agent_place_info_visible)
  980. {
  981. // We don't need to teleport to the current location
  982. // so check if the location is not within the current parcel.
  983. mTeleportBtn->setEnabled(have_3d_pos &&
  984. !LLViewerParcelMgr::getInstance()->inAgentParcel(mPosGlobal));
  985. }
  986. else if (mPlaceInfoType == LANDMARK_INFO_TYPE || mPlaceInfoType == REMOTE_PLACE_INFO_TYPE)
  987. {
  988. mTeleportBtn->setEnabled(have_3d_pos);
  989. }
  990. }
  991. else
  992. {
  993. if (mActivePanel)
  994. mActivePanel->updateVerbs();
  995. }
  996. }
  997. LLPanelPlaceInfo* LLPanelPlaces::getCurrentInfoPanel()
  998. {
  999. if (mPlaceInfoType == AGENT_INFO_TYPE ||
  1000. mPlaceInfoType == REMOTE_PLACE_INFO_TYPE ||
  1001. mPlaceInfoType == TELEPORT_HISTORY_INFO_TYPE)
  1002. {
  1003. return mPlaceProfile;
  1004. }
  1005. else if (mPlaceInfoType == CREATE_LANDMARK_INFO_TYPE ||
  1006. mPlaceInfoType == LANDMARK_INFO_TYPE ||
  1007. mPlaceInfoType == LANDMARK_TAB_INFO_TYPE)
  1008. {
  1009. return mLandmarkInfo;
  1010. }
  1011. return NULL;
  1012. }
  1013. static bool is_agent_in_selected_parcel(LLParcel* parcel)
  1014. {
  1015. LLViewerParcelMgr* parcel_mgr = LLViewerParcelMgr::getInstance();
  1016. LLViewerRegion* region = parcel_mgr->getSelectionRegion();
  1017. if (!region || !parcel)
  1018. return false;
  1019. return region == gAgent.getRegion() &&
  1020. parcel->getLocalID() == parcel_mgr->getAgentParcel()->getLocalID();
  1021. }
  1022. static void onSLURLBuilt(std::string& slurl)
  1023. {
  1024. LLView::getWindow()->copyTextToClipboard(utf8str_to_wstring(slurl));
  1025. LLSD args;
  1026. args["SLURL"] = slurl;
  1027. LLNotificationsUtil::add("CopySLURL", args);
  1028. }