/**
 * @file llpanelplaces.cpp
 * @brief Side Bar "Places" panel
 *
 * $LicenseInfo:firstyear=2009&license=viewergpl$
 *
 * Copyright (c) 2004-2009, Linden Research, Inc.
 *
 * Second Life Viewer Source Code
 * The source code in this file ("Source Code") is provided by Linden Lab
 * to you under the terms of the GNU General Public License, version 2.0
 * ("GPL"), unless you have obtained a separate licensing agreement
 * ("Other License"), formally executed by you and Linden Lab.  Terms of
 * the GPL can be found in doc/GPL-license.txt in this distribution, or
 * online at http://secondlifegrid.net/programs/open_source/licensing/gplv2
 *
 * There are special exceptions to the terms and conditions of the GPL as
 * it is applied to this Source Code. View the full text of the exception
 * in the file doc/FLOSS-exception.txt in this software distribution, or
 * online at http://secondlifegrid.net/programs/open_source/licensing/flossexception
 *
 * By copying, modifying or distributing this software, you acknowledge
 * that you have read and understood your obligations described above,
 * and agree to abide by those obligations.
 *
 * ALL LINDEN LAB SOURCE CODE IS PROVIDED "AS IS." LINDEN LAB MAKES NO
 * WARRANTIES, EXPRESS, IMPLIED OR OTHERWISE, REGARDING ITS ACCURACY,
 * COMPLETENESS OR PERFORMANCE.
 * $/LicenseInfo$
 */

#include "llviewerprecompiledheaders.h"

#include "llassettype.h"
#include "llwindow.h"

#include "lllandmark.h"

#include "llfloaterreg.h"
#include "llnotifications.h"
#include "llfiltereditor.h"
#include "lltabcontainer.h"
#include "lltrans.h"
#include "lluictrlfactory.h"

#include "llagent.h"
#include "lllandmarkactions.h"
#include "lllandmarklist.h"
#include "llfloaterworldmap.h"
#include "llpanelplaces.h"
#include "llpanellandmarks.h"
#include "llpanelteleporthistory.h"
#include "llsidetray.h"
#include "lltoggleablemenu.h"
#include "llviewerinventory.h"
#include "llviewermenu.h"
#include "llviewerparcelmgr.h"
#include "llviewerregion.h"

static const S32 LANDMARK_FOLDERS_MENU_WIDTH = 250;
static const std::string AGENT_INFO_TYPE			= "agent";
static const std::string CREATE_LANDMARK_INFO_TYPE	= "create_landmark";
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";

// Helper functions
static bool cmp_folders(const folder_pair_t& left, const folder_pair_t& right);
static std::string getFullFolderName(const LLViewerInventoryCategory* cat);
static void collectLandmarkFolders(LLInventoryModel::cat_array_t& cats);
static const LLVector3 get_pos_local_from_global(const LLVector3d &pos_global);
static void onSLURLBuilt(std::string& slurl);

static LLRegisterPanelClassWrapper<LLPanelPlaces> t_places("panel_places");

LLPanelPlaces::LLPanelPlaces()
	:	LLPanel(),
		mFilterSubString(LLStringUtil::null),
		mActivePanel(NULL),
		mFilterEditor(NULL),
		mPlaceInfo(NULL),
		mItem(NULL),
		mPlaceMenu(NULL),
		mLandmarkMenu(NULL),		
		mLandmarkFoldersMenuHandle(),
		mPosGlobal()
{
	gInventory.addObserver(this);

	LLViewerParcelMgr::getInstance()->addAgentParcelChangedCallback(
			boost::bind(&LLPanelPlaces::onAgentParcelChange, this));

	//LLUICtrlFactory::getInstance()->buildPanel(this, "panel_places.xml"); // Called from LLRegisterPanelClass::defaultPanelClassBuilder()
}

LLPanelPlaces::~LLPanelPlaces()
{
	if (gInventory.containsObserver(this))
		gInventory.removeObserver(this);
	
	LLView::deleteViewByHandle(mLandmarkFoldersMenuHandle);
}

BOOL LLPanelPlaces::postBuild()
{
	mCreateLandmarkBtn = getChild<LLButton>("create_landmark_btn");
	mCreateLandmarkBtn->setClickedCallback(boost::bind(&LLPanelPlaces::onCreateLandmarkButtonClicked, this, LLUUID()));
	
	mFolderMenuBtn = getChild<LLButton>("folder_menu_btn");
	mFolderMenuBtn->setClickedCallback(boost::bind(&LLPanelPlaces::showLandmarkFoldersMenu, this));

	mTeleportBtn = getChild<LLButton>("teleport_btn");
	mTeleportBtn->setClickedCallback(boost::bind(&LLPanelPlaces::onTeleportButtonClicked, this));
	
	mShowOnMapBtn = getChild<LLButton>("map_btn");
	mShowOnMapBtn->setClickedCallback(boost::bind(&LLPanelPlaces::onShowOnMapButtonClicked, this));
	
	mShareBtn = getChild<LLButton>("share_btn");
	//mShareBtn->setClickedCallback(boost::bind(&LLPanelPlaces::onShareButtonClicked, this));

	mOverflowBtn = getChild<LLButton>("overflow_btn");

	// *TODO: Assign the action to an appropriate event.
	//mOverflowBtn->setClickedCallback(boost::bind(&LLPanelPlaces::toggleMediaPanel, this));
	mOverflowBtn->setClickedCallback(boost::bind(&LLPanelPlaces::onOverflowButtonClicked, this));

	LLUICtrl::CommitCallbackRegistry::ScopedRegistrar registrar;
	registrar.add("Places.OverflowMenu.Action",  boost::bind(&LLPanelPlaces::onOverflowMenuItemClicked, this, _2));

	mPlaceMenu = LLUICtrlFactory::getInstance()->createFromFile<LLToggleableMenu>("menu_place.xml", gMenuHolder, LLViewerMenuHolderGL::child_registry_t::instance());
	if (!mPlaceMenu)
	{
		llwarns << "Error loading Place menu" << llendl;
	}

	mLandmarkMenu = LLUICtrlFactory::getInstance()->createFromFile<LLToggleableMenu>("menu_landmark.xml", gMenuHolder, LLViewerMenuHolderGL::child_registry_t::instance());
	if (!mLandmarkMenu)
	{
		llwarns << "Error loading Landmark menu" << llendl;
	}

	mTabContainer = getChild<LLTabContainer>("Places Tabs");
	if (mTabContainer)
	{
		mTabContainer->setCommitCallback(boost::bind(&LLPanelPlaces::onTabSelected, this));
	}

	mFilterEditor = getChild<LLFilterEditor>("Filter");
	if (mFilterEditor)
	{
		mFilterEditor->setCommitCallback(boost::bind(&LLPanelPlaces::onFilterEdit, this, _2));
	}

	mPlaceInfo = getChild<LLPanelPlaceInfo>("panel_place_info");
	
	LLButton* back_btn = mPlaceInfo->getChild<LLButton>("back_btn");
	back_btn->setClickedCallback(boost::bind(&LLPanelPlaces::onBackButtonClicked, this));

	// *TODO: Assign the action to an appropriate event.
	mOverflowBtn->setClickedCallback(boost::bind(&LLPanelPlaces::toggleMediaPanel, this));

	return TRUE;
}

void LLPanelPlaces::onOpen(const LLSD& key)
{
	mFilterEditor->clear();
	onFilterEdit("");
	
	if(mPlaceInfo == NULL || key.size() == 0)
		return;

	mPlaceInfoType = key["type"].asString();
	mPosGlobal.setZero();
	togglePlaceInfoPanel(TRUE);
	updateVerbs();

	if (mPlaceInfoType == AGENT_INFO_TYPE)
	{
		mPlaceInfo->setInfoType(LLPanelPlaceInfo::AGENT);
		mPlaceInfo->displayAgentParcelInfo();
		
		mPosGlobal = gAgent.getPositionGlobal();
	}
	else if (mPlaceInfoType == CREATE_LANDMARK_INFO_TYPE)
	{
		mPlaceInfo->setInfoType(LLPanelPlaceInfo::CREATE_LANDMARK);
		mPlaceInfo->displayAgentParcelInfo();
		
		mPosGlobal = gAgent.getPositionGlobal();
	}
	else if (mPlaceInfoType == LANDMARK_INFO_TYPE)
	{
		LLUUID item_uuid = key["id"].asUUID();
		LLInventoryItem* item = gInventory.getItem(item_uuid);
		if (!item)
			return;
		
		setItem(item);
	}
	else if (mPlaceInfoType == REMOTE_PLACE_INFO_TYPE)
	{
		if (mPlaceInfo->isMediaPanelVisible())
		{
			toggleMediaPanel();
		}

		mPosGlobal = LLVector3d(key["x"].asReal(),
								key["y"].asReal(),
								key["z"].asReal());

		mPlaceInfo->setInfoType(LLPanelPlaceInfo::PLACE);
		mPlaceInfo->displayParcelInfo(get_pos_local_from_global(mPosGlobal),
									  LLUUID(),
									  mPosGlobal);
	}
	else if (mPlaceInfoType == TELEPORT_HISTORY_INFO_TYPE)
	{
		S32 index = key["id"].asInteger();

		const LLTeleportHistory::slurl_list_t& hist_items =
			LLTeleportHistory::getInstance()->getItems();

		mPosGlobal = hist_items[index].mGlobalPos;

		mPlaceInfo->setInfoType(LLPanelPlaceInfo::TELEPORT_HISTORY);
		mPlaceInfo->displayParcelInfo(get_pos_local_from_global(mPosGlobal),
									  hist_items[index].mRegionID,
									  mPosGlobal);
	}
	

}

void LLPanelPlaces::setItem(LLInventoryItem* item)
{
	if (!mPlaceInfo)
		return;

	mItem = item;
	
	// If the item is a link get a linked item
	if (mItem->getType() == LLAssetType::AT_LINK)
	{
		mItem = gInventory.getItem(mItem->getAssetUUID());
		if (mItem.isNull())
			return;
	}

	mPlaceInfo->setInfoType(LLPanelPlaceInfo::LANDMARK);
	mPlaceInfo->displayItemInfo(mItem);

	LLLandmark* lm = gLandmarkList.getAsset(mItem->getAssetUUID(),
											boost::bind(&LLPanelPlaces::onLandmarkLoaded, this, _1));
	if (lm)
	{
		onLandmarkLoaded(lm);
	}
}

void LLPanelPlaces::onLandmarkLoaded(LLLandmark* landmark)
{
	if (!mPlaceInfo)
		return;

	LLUUID region_id;
	landmark->getRegionID(region_id);
	landmark->getGlobalPos(mPosGlobal);
	mPlaceInfo->displayParcelInfo(landmark->getRegionPos(),
								  region_id,
								  mPosGlobal);
}

void LLPanelPlaces::onFilterEdit(const std::string& search_string)
{
	if (mFilterSubString != search_string)
	{
		mFilterSubString = search_string;

		// Searches are case-insensitive
		LLStringUtil::toUpper(mFilterSubString);
		LLStringUtil::trimHead(mFilterSubString);

		mActivePanel->onSearchEdit(mFilterSubString);
	}
}

void LLPanelPlaces::onTabSelected()
{
	mActivePanel = dynamic_cast<LLPanelPlacesTab*>(mTabContainer->getCurrentPanel());
	if (!mActivePanel)
		return;

	onFilterEdit(mFilterSubString);	
	mActivePanel->updateVerbs();
}

/*
void LLPanelPlaces::onShareButtonClicked()
{
	// TODO: Launch the "Things" Share wizard
}
*/

void LLPanelPlaces::onTeleportButtonClicked()
{
	if (!mPlaceInfo)
		return;

	if (mPlaceInfo->getVisible())
	{
		if (mPlaceInfoType == LANDMARK_INFO_TYPE)
		{
			LLSD payload;
			payload["asset_id"] = mItem->getAssetUUID();
			LLNotifications::instance().add("TeleportFromLandmark", LLSD(), 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
	{
		mActivePanel->onTeleport();
	}
}

void LLPanelPlaces::onShowOnMapButtonClicked()
{
	if (!mPlaceInfo)
		return;

	if (mPlaceInfo->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)
		{
			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
	{
		mActivePanel->onShowOnMap();
	}
}

void LLPanelPlaces::onOverflowButtonClicked()
{
	LLToggleableMenu* menu;

	bool is_agent_place_info_visible = mPlaceInfoType == AGENT_INFO_TYPE;

	if ((is_agent_place_info_visible ||
		 mPlaceInfoType == "remote_place" ||
		 mPlaceInfoType == "teleport_history") && mPlaceMenu != NULL)
	{
		menu = mPlaceMenu;
		
		// 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(is_agent_place_info_visible &&
																 !LLLandmarkActions::landmarkAlreadyExists());
	}
	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(LLAssetType::AT_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;
	}

	if (!menu->toggleVisibility())
		return;

	menu->updateParent(LLMenuGL::sMenuContainer);
	LLRect rect = mOverflowBtn->getRect();
	menu->setButtonRect(rect, this);
	LLMenuGL::showPopup(this, menu, rect.mRight, rect.mTop);
}

void LLPanelPlaces::onOverflowMenuItemClicked(const LLSD& param)
{
	std::string item = param.asString();
	if (item == "landmark")
	{
		onOpen(LLSD().insert("type", CREATE_LANDMARK_INFO_TYPE));
	}
	else if (item == "copy")
	{
		LLLandmarkActions::getSLURLfromPosGlobal(mPosGlobal, boost::bind(&onSLURLBuilt, _1));
	}
	else if (item == "delete")
	{
		gInventory.removeItem(mItem->getUUID());

		onBackButtonClicked();
	}
	else if (item == "pick")
	{
		if (!mPlaceInfo)
			return;
		
		mPlaceInfo->createPick(mPosGlobal);

		onBackButtonClicked();
	}
}

void LLPanelPlaces::onCreateLandmarkButtonClicked(const LLUUID& folder_id)
{
	if (!mPlaceInfo)
		return;

	mPlaceInfo->createLandmark(folder_id);
}

void LLPanelPlaces::onBackButtonClicked()
{
	if (!mPlaceInfo)
		return;
	
	if (mPlaceInfo->isMediaPanelVisible())
	{
		toggleMediaPanel();
	}
	else
	{
		togglePlaceInfoPanel(FALSE);

		// Resetting mPlaceInfoType when Place Info panel is closed.
		mPlaceInfoType = LLStringUtil::null;
	}

	updateVerbs();
}

void LLPanelPlaces::toggleMediaPanel()
{
	if (!mPlaceInfo)
		return;

	mPlaceInfo->toggleMediaPanel(!mPlaceInfo->isMediaPanelVisible());

	// Refresh the current place info because
	// the media panel controls can't refer to
	// the remote parcel media.
	onOpen(LLSD().insert("type", AGENT_INFO_TYPE));
}

void LLPanelPlaces::togglePlaceInfoPanel(BOOL visible)
{
	if (!mPlaceInfo)
		return;

	mPlaceInfo->setVisible(visible);
	mFilterEditor->setVisible(!visible);
	mTabContainer->setVisible(!visible);

	if (visible)
	{
		mPlaceInfo->resetLocation();

		LLRect rect = getRect();
		LLRect new_rect = LLRect(rect.mLeft, rect.mTop, rect.mRight, mTabContainer->getRect().mBottom);
		mPlaceInfo->reshape(new_rect.getWidth(),new_rect.getHeight());
	}
}

//virtual
void LLPanelPlaces::changed(U32 mask)
{
	if (!(gInventory.isInventoryUsable() && LLTeleportHistory::getInstance()))
		return;

	LLLandmarksPanel* landmarks_panel = new LLLandmarksPanel();
	if (landmarks_panel)
	{
		landmarks_panel->setPanelPlacesButtons(this);

		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)
	{
		teleport_history_panel->setPanelPlacesButtons(this);

		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());

	// we don't need to monitor inventory changes anymore,
	// so remove the observer
	gInventory.removeObserver(this);
}

void LLPanelPlaces::onAgentParcelChange()
{
	if (!mPlaceInfo)
		return;

	if (mPlaceInfo->getVisible() && mPlaceInfoType == CREATE_LANDMARK_INFO_TYPE)
	{
		onOpen(LLSD().insert("type", mPlaceInfoType));
	}
	else if (mPlaceInfo->isMediaPanelVisible())
	{
		onOpen(LLSD().insert("type", AGENT_INFO_TYPE));
	}
	else
	{
		updateVerbs();
	}
}

void LLPanelPlaces::updateVerbs()
{
	if (!mPlaceInfo)
		return;

	bool is_place_info_visible = mPlaceInfo->getVisible();
	bool is_agent_place_info_visible = mPlaceInfoType == AGENT_INFO_TYPE;
	bool is_create_landmark_visible = mPlaceInfoType == CREATE_LANDMARK_INFO_TYPE;
	bool is_media_panel_visible = mPlaceInfo->isMediaPanelVisible();
	
	mTeleportBtn->setVisible(!is_create_landmark_visible);
	mShareBtn->setVisible(!is_create_landmark_visible);
	mCreateLandmarkBtn->setVisible(is_create_landmark_visible);
	mFolderMenuBtn->setVisible(is_create_landmark_visible);

	mOverflowBtn->setEnabled(is_place_info_visible && !is_media_panel_visible && !is_create_landmark_visible);

	if (is_place_info_visible)
	{
		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(!is_media_panel_visible &&
									 !mPosGlobal.isExactlyZero() &&
									 !LLViewerParcelMgr::getInstance()->inAgentParcel(mPosGlobal));
		}
		else if (is_create_landmark_visible)
		{
			// Enable "Create Landmark" only if there is no landmark
			// for the current parcel.
			bool no_landmark = !LLLandmarkActions::landmarkAlreadyExists();
			mCreateLandmarkBtn->setEnabled(no_landmark);
			mFolderMenuBtn->setEnabled(no_landmark);
		}
		else if (mPlaceInfoType == LANDMARK_INFO_TYPE || mPlaceInfoType == REMOTE_PLACE_INFO_TYPE)
		{
			mTeleportBtn->setEnabled(TRUE);
		}

		mShowOnMapBtn->setEnabled(!is_media_panel_visible);
	}
	else
	{
		mActivePanel->updateVerbs();
	}
}

void LLPanelPlaces::showLandmarkFoldersMenu()
{
	if (mLandmarkFoldersMenuHandle.isDead())
	{
		LLToggleableMenu::Params menu_p;
		menu_p.name("landmarks_folders_menu");
		menu_p.can_tear_off(false);
		menu_p.visible(false);
		menu_p.scrollable(true);
		menu_p.max_scrollable_items = 10;

		LLToggleableMenu* menu = LLUICtrlFactory::create<LLToggleableMenu>(menu_p);

		mLandmarkFoldersMenuHandle = menu->getHandle();
	}

	LLToggleableMenu* menu = (LLToggleableMenu*)mLandmarkFoldersMenuHandle.get();
	if(!menu)
		return;

	if (!menu->toggleVisibility())
		return;

	// Collect all folders that can contain landmarks.
	LLInventoryModel::cat_array_t cats;
	collectLandmarkFolders(cats);

	// Sort the folders by their full name.
	folder_vec_t folders;
	S32 count = cats.count();
	for (S32 i = 0; i < count; i++)
	{
		const LLViewerInventoryCategory* cat = cats.get(i);
		std::string cat_full_name = getFullFolderName(cat);
		folders.push_back(folder_pair_t(cat->getUUID(), cat_full_name));
	}
	sort(folders.begin(), folders.end(), cmp_folders);

	LLRect btn_rect = mFolderMenuBtn->getRect();

	LLRect root_rect = getRootView()->getRect();
	
	// Check it there are changed items or viewer dimensions 
	// have changed since last call
	if (mLandmarkFoldersCache.size() == count &&
		mRootViewWidth == root_rect.getWidth() &&
		mRootViewHeight == root_rect.getHeight())
	{
		S32 i;
		for (i = 0; i < count; i++)
		{
			if (mLandmarkFoldersCache[i].second != folders[i].second)
			{
				break;
			}
		}

		// Check passed, just show the menu
		if (i == count)
		{
			menu->buildDrawLabels();
			menu->updateParent(LLMenuGL::sMenuContainer);

			menu->setButtonRect(btn_rect, this);
			LLMenuGL::showPopup(this, menu, btn_rect.mRight, btn_rect.mTop);
			return;
		}
	}

	// If there are changes, store the new viewer dimensions
	// and a list of folders
	mRootViewWidth = root_rect.getWidth();
	mRootViewHeight = root_rect.getHeight();
	mLandmarkFoldersCache = folders;

	menu->empty();

	// Menu width must not exceed the root view limits,
	// so we assume the space between the left edge of
	// the root view and 
	LLRect screen_btn_rect;
	localRectToScreen(btn_rect, &screen_btn_rect);
	S32 free_space = screen_btn_rect.mRight;
	U32 max_width = llmin(LANDMARK_FOLDERS_MENU_WIDTH, free_space);

	for(folder_vec_t::const_iterator it = mLandmarkFoldersCache.begin(); it != mLandmarkFoldersCache.end(); it++)
	{
		const std::string& item_name = it->second;

		LLMenuItemCallGL::Params item_params;
		item_params.name(item_name);
		item_params.label(item_name);

		item_params.on_click.function(boost::bind(&LLPanelPlaces::onCreateLandmarkButtonClicked, this, it->first));

		LLMenuItemCallGL *menu_item = LLUICtrlFactory::create<LLMenuItemCallGL>(item_params);

		// *TODO: Use a separate method for menu width calculation.
		// Check whether item name wider than menu
		if (menu_item->getNominalWidth() > max_width)
		{
			S32 chars_total = item_name.length();
			S32 chars_fitted = 1;
			menu_item->setLabel(LLStringExplicit(""));
			S32 label_space = max_width - menu_item->getFont()->getWidth("...") -
				menu_item->getNominalWidth(); // This returns width of menu item with empty label (pad pixels)

			while (chars_fitted < chars_total && menu_item->getFont()->getWidth(item_name, 0, chars_fitted) < label_space)
			{
				chars_fitted++;
			}
			chars_fitted--; // Rolling back one char, that doesn't fit

			menu_item->setLabel(item_name.substr(0, chars_fitted) + "...");
		}

		menu->addChild(menu_item);
	}

	menu->buildDrawLabels();
	menu->updateParent(LLMenuGL::sMenuContainer);
	menu->setButtonRect(btn_rect, this);
	LLMenuGL::showPopup(this, menu, btn_rect.mRight, btn_rect.mTop);
}

static bool cmp_folders(const folder_pair_t& left, const folder_pair_t& right)
{
	return left.second < right.second;
}

static std::string getFullFolderName(const LLViewerInventoryCategory* cat)
{
	std::string name = cat->getName();
	LLUUID parent_id;

	// translate category name, if it's right below the root
	// FIXME: it can throw notification about non existent string in strings.xml
	if (cat->getParentUUID().notNull() && cat->getParentUUID() == gInventory.getRootFolderID())
	{
		LLTrans::findString(name, "InvFolder " + name);
	}

	// we don't want "My Inventory" to appear in the name
	while ((parent_id = cat->getParentUUID()).notNull() && parent_id != gInventory.getRootFolderID())
	{
		cat = gInventory.getCategory(parent_id);
		name = cat->getName() + "/" + name;
	}

	return name;
}

static void collectLandmarkFolders(LLInventoryModel::cat_array_t& cats)
{
	// Add the "Landmarks" category itself.
	LLUUID landmarks_id = gInventory.findCategoryUUIDForType(LLAssetType::AT_LANDMARK);
	LLViewerInventoryCategory* landmarks_cat = gInventory.getCategory(landmarks_id);
	if (!landmarks_cat)
	{
		llwarns << "Cannot find the landmarks folder" << llendl;
	}
	else
	{
		cats.put(landmarks_cat);
	}

	// Add descendent folders of the "Landmarks" category.
	LLInventoryModel::item_array_t items; // unused
	LLIsType is_category(LLAssetType::AT_CATEGORY);
	gInventory.collectDescendentsIf(
		landmarks_id,
		cats,
		items,
		LLInventoryModel::EXCLUDE_TRASH,
		is_category);

	// Add the "My Favorites" category.
	LLUUID favorites_id = gInventory.findCategoryUUIDForType(LLAssetType::AT_FAVORITE);
	LLViewerInventoryCategory* favorites_cat = gInventory.getCategory(favorites_id);
	if (!favorites_cat)
	{
		llwarns << "Cannot find the favorites folder" << llendl;
	}
	else
	{
		cats.put(favorites_cat);
	}
}

static const LLVector3 get_pos_local_from_global(const LLVector3d &pos_global)
{
	F32 region_x = (F32)fmod( pos_global.mdV[VX], (F64)REGION_WIDTH_METERS );
	F32 region_y = (F32)fmod( pos_global.mdV[VY], (F64)REGION_WIDTH_METERS );

	LLVector3 pos_local(region_x, region_y, (F32)pos_global.mdV[VZ]);
	return pos_local;
}

static void onSLURLBuilt(std::string& slurl)
{
	LLView::getWindow()->copyTextToClipboard(utf8str_to_wstring(slurl));
		
	LLSD args;
	args["SLURL"] = slurl;

	LLNotifications::instance().add("CopySLURL", args);
}
