PageRenderTime 956ms CodeModel.GetById 182ms app.highlight 611ms RepoModel.GetById 133ms app.codeStats 1ms

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