/** 
 * @file fsfloatercontacts.cpp
 * @brief Legacy contacts floater implementation
 *
 * $LicenseInfo:firstyear=2011&license=fsviewerlgpl$
 * Phoenix Firestorm Viewer Source Code
 * Copyright (C) 2013, The Phoenix Firestorm Project, Inc.
 * 
 * This library is free software; you can redistribute it and/or
 * modify it under the terms of the GNU Lesser General Public
 * License as published by the Free Software Foundation;
 * version 2.1 of the License only.
 * 
 * This library is distributed in the hope that it will be useful,
 * but WITHOUT ANY WARRANTY; without even the implied warranty of
 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
 * Lesser General Public License for more details.
 * 
 * You should have received a copy of the GNU Lesser General Public
 * License along with this library; if not, write to the Free Software
 * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA  02110-1301  USA
 * 
 * The Phoenix Firestorm Project, Inc., 1831 Oakwood Drive, Fairmont, Minnesota 56031-3225 USA
 * http://www.firestormviewer.org
 * $/LicenseInfo$
 */

#include "llviewerprecompiledheaders.h"

#include "fsfloatercontacts.h"

#include "fscommon.h"
#include "fscontactsfriendsmenu.h"
#include "fsfloaterimcontainer.h"
#include "fsscrolllistctrl.h"
#include "llagent.h"
#include "llavataractions.h"
#include "llfiltereditor.h"
#include "llfloateravatarpicker.h"
#include "llfloatergroupinvite.h"
#include "llfloaterreg.h"
#include "llgroupactions.h"
#include "llgrouplist.h"
#include "llnotificationsutil.h"
#include "llscrolllistctrl.h"
#include "llslurl.h"
#include "llstartup.h"
#include "lltabcontainer.h"
#include "lltooldraganddrop.h"
#include "llviewermenu.h"
#include "llvoiceclient.h"
// [RLVa:KB] - @pay
#include "rlvactions.h"
// [/RLVa:KB]

//Maximum number of people you can select to do an operation on at once.
const U32 MAX_FRIEND_SELECT = 20;
const F32 RIGHTS_CHANGE_TIMEOUT = 5.f;

static const std::string FRIENDS_TAB_NAME	= "friends_panel";
static const std::string GROUP_TAB_NAME		= "groups_panel";


//
// FSFloaterContacts
//

FSFloaterContacts::FSFloaterContacts(const LLSD& seed)
	: LLFloater(seed),
	LLEventTimer(300.f),
	mTabContainer(NULL),
	mFriendsList(NULL),
	mGroupList(NULL),
	mFriendFilter(NULL),
	mGroupFilter(NULL),
	mFriendFilterSubString(LLStringUtil::null),
	mFriendFilterSubStringOrig(LLStringUtil::null),
	mAllowRightsChange(true),
	mRightsChangeNotificationTriggered(false),
	mNumRightsChanged(0),
	mRlvBehaviorCallbackConnection(),
	mResetLastColumnDisplayModeChanged(false),
	mDirtyNames(true)
{
	LLAvatarTracker::instance().addObserver(this);
	// For notification when SIP online status changes.
	LLVoiceClient::getInstance()->addObserver(this);
}

FSFloaterContacts::~FSFloaterContacts()
{
	// For notification when SIP online status changes.
	LLVoiceClient::getInstance()->removeObserver(this);
	LLAvatarTracker::instance().removeObserver(this);

	if (mRlvBehaviorCallbackConnection.connected())
	{
		mRlvBehaviorCallbackConnection.disconnect();
	}

	for (avatar_name_cb_t::iterator it = mAvatarNameCacheConnections.begin(); it != mAvatarNameCacheConnections.end(); ++it)
	{
		if (it->second.connected())
		{
			it->second.disconnect();
		}
	}
	mAvatarNameCacheConnections.clear();
}

BOOL FSFloaterContacts::postBuild()
{
	mTabContainer = getChild<LLTabContainer>("friends_and_groups");
	mFriendsTab = getChild<LLPanel>(FRIENDS_TAB_NAME);
	mFriendListFontName = mFriendsTab->getString("FontName");

	mFriendsList = mFriendsTab->getChild<FSScrollListCtrl>("friend_list");
	mFriendsList->setMaxSelectable(MAX_FRIEND_SELECT);
	mFriendsList->setCommitOnSelectionChange(TRUE);
	mFriendsList->setCommitCallback(boost::bind(&FSFloaterContacts::onSelectName, this));
	mFriendsList->setDoubleClickCallback(boost::bind(&FSFloaterContacts::onImButtonClicked, this));
	mFriendsList->setHandleDaDCallback(boost::bind(&FSFloaterContacts::handleFriendsListDragAndDrop, this, _1, _2, _3, _4, _5, _6, _7, _8));
	mFriendsList->setContextMenu(&gFSContactsFriendsMenu);
	mFriendsList->setFilterColumn(mFriendsList->getColumn("full_name")->mIndex);
	
	mFriendsTab->childSetAction("im_btn",				boost::bind(&FSFloaterContacts::onImButtonClicked,				this));
	mFriendsTab->childSetAction("profile_btn",			boost::bind(&FSFloaterContacts::onViewProfileButtonClicked,		this));
	mFriendsTab->childSetAction("offer_teleport_btn",	boost::bind(&FSFloaterContacts::onTeleportButtonClicked,		this));
	mFriendsTab->childSetAction("map_btn", 				boost::bind(&FSFloaterContacts::onMapButtonClicked,				this));
	mFriendsTab->childSetAction("pay_btn",				boost::bind(&FSFloaterContacts::onPayButtonClicked,				this));
	mFriendsTab->childSetAction("remove_btn",			boost::bind(&FSFloaterContacts::onDeleteFriendButtonClicked,	this));
	mFriendsTab->childSetAction("add_btn",				boost::bind(&FSFloaterContacts::onAddFriendWizButtonClicked,	this, _1));
	mFriendsTab->setDefaultBtn("im_btn");

	mFriendsTab->getChild<LLTextBox>("friend_count")->setTextArg("COUNT", llformat("%d", mFriendsList->getItemCount()));
	mFriendFilter = mFriendsTab->getChild<LLFilterEditor>("friend_filter_input");
	mFriendFilter->setCommitCallback(boost::bind(&FSFloaterContacts::onFriendFilterEdit, this, _2));

	mGroupsTab = getChild<LLPanel>(GROUP_TAB_NAME);
	mGroupList = mGroupsTab->getChild<LLGroupList>("group_list");
	mGroupList->setNoItemsMsg(getString("no_groups_msg"));
	mGroupList->setNoFilteredItemsMsg(getString("no_filtered_groups_msg"));
	
	mGroupList->setDoubleClickCallback(boost::bind(&FSFloaterContacts::onGroupChatButtonClicked, this));
	mGroupList->setCommitCallback(boost::bind(&FSFloaterContacts::updateGroupButtons, this));
	mGroupList->setReturnCallback(boost::bind(&FSFloaterContacts::onGroupChatButtonClicked, this));
	
	mGroupsTab->childSetAction("chat_btn",		boost::bind(&FSFloaterContacts::onGroupChatButtonClicked,		this));
	mGroupsTab->childSetAction("info_btn",		boost::bind(&FSFloaterContacts::onGroupInfoButtonClicked,		this));
	mGroupsTab->childSetAction("activate_btn",	boost::bind(&FSFloaterContacts::onGroupActivateButtonClicked,	this));
	mGroupsTab->childSetAction("leave_btn",		boost::bind(&FSFloaterContacts::onGroupLeaveButtonClicked,		this));
	mGroupsTab->childSetAction("create_btn",	boost::bind(&FSFloaterContacts::onGroupCreateButtonClicked,		this));
	mGroupsTab->childSetAction("search_btn",	boost::bind(&FSFloaterContacts::onGroupSearchButtonClicked,		this));
	mGroupsTab->childSetAction("titles_btn",	boost::bind(&FSFloaterContacts::onGroupTitlesButtonClicked,		this));
	mGroupsTab->childSetAction("invite_btn",	boost::bind(&FSFloaterContacts::onGroupInviteButtonClicked,		this));
	mGroupsTab->setDefaultBtn("chat_btn");

	mGroupFilter = mGroupsTab->getChild<LLFilterEditor>("group_filter_input");
	mGroupFilter->setCommitCallback(boost::bind(&FSFloaterContacts::onGroupFilterEdit, this, _2));

	mRlvBehaviorCallbackConnection = gRlvHandler.setBehaviourCallback(boost::bind(&FSFloaterContacts::updateRlvRestrictions, this, _1));

	gSavedSettings.getControl("FSFriendListFullNameFormat")->getSignal()->connect(boost::bind(&FSFloaterContacts::onDisplayNameChanged, this));
	gSavedSettings.getControl("FSFriendListSortOrder")->getSignal()->connect(boost::bind(&FSFloaterContacts::sortFriendList, this));
	gSavedSettings.getControl("FSFriendListColumnShowUserName")->getSignal()->connect(boost::bind(&FSFloaterContacts::onColumnDisplayModeChanged, this, "FSFriendListColumnShowUserName"));
	gSavedSettings.getControl("FSFriendListColumnShowDisplayName")->getSignal()->connect(boost::bind(&FSFloaterContacts::onColumnDisplayModeChanged, this, "FSFriendListColumnShowDisplayName"));
	gSavedSettings.getControl("FSFriendListColumnShowFullName")->getSignal()->connect(boost::bind(&FSFloaterContacts::onColumnDisplayModeChanged, this, "FSFriendListColumnShowFullName"));
	gSavedSettings.getControl("FSFriendListColumnShowPermissions")->getSignal()->connect(boost::bind(&FSFloaterContacts::onColumnDisplayModeChanged, this, std::string()));
	onColumnDisplayModeChanged();

	LLAvatarNameCache::getInstance()->addUseDisplayNamesCallback(boost::bind(&FSFloaterContacts::onDisplayNameChanged, this));

	return TRUE;
}

void FSFloaterContacts::draw()
{
	if (mResetLastColumnDisplayModeChanged)
	{
		gSavedSettings.setBOOL(mLastColumnDisplayModeChanged, TRUE);
		mResetLastColumnDisplayModeChanged = false;
	}

	if (mDirtyNames)
	{
		onDisplayNameChanged();
		mFriendsList->setNeedsSort();
		mDirtyNames = false;
	}

	LLFloater::draw();
}

BOOL FSFloaterContacts::tick()
{
	onDisplayNameChanged();
	return FALSE;
}

BOOL FSFloaterContacts::handleKeyHere(KEY key, MASK mask)
{
	if (FSCommon::isFilterEditorKeyCombo(key, mask))
	{
		if (getActiveTabName() == FRIENDS_TAB_NAME && gSavedSettings.getBOOL("FSContactListShowSearch"))
		{
			mFriendFilter->setFocus(TRUE);
			return TRUE;
		}
		else if (getActiveTabName() == GROUP_TAB_NAME)
		{
			mGroupFilter->setFocus(TRUE);
			return true;
		}
	}

	if (mask == MASK_CONTROL && key == 'W' && getHost())
	{
		getHost()->closeFloater();
		return TRUE;
	}

	return LLFloater::handleKeyHere(key, mask);
}

void FSFloaterContacts::updateGroupButtons()
{
	LLUUID groupId = getCurrentItemID();
	bool isGroup = groupId.notNull();

	mGroupsTab->getChild<LLUICtrl>("groupcount")->setValue(FSCommon::populateGroupCount());
	
	getChildView("chat_btn")->setEnabled(isGroup && gAgent.hasPowerInGroup(groupId, GP_SESSION_JOIN));
	getChildView("info_btn")->setEnabled(isGroup);
	getChildView("activate_btn")->setEnabled(groupId != gAgent.getGroupID());
	getChildView("leave_btn")->setEnabled(isGroup);
	getChildView("create_btn")->setEnabled((!gMaxAgentGroups) || (gAgent.mGroups.size() < gMaxAgentGroups));
	getChildView("invite_btn")->setEnabled(isGroup && gAgent.hasPowerInGroup(groupId, GP_MEMBER_INVITE));
}

void FSFloaterContacts::onOpen(const LLSD& key)
{
	FSFloaterIMContainer* floater_container = FSFloaterIMContainer::getInstance();
	if (gSavedSettings.getBOOL("ContactsTornOff"))
	{
		// first set the tear-off host to the conversations container
		setHost(floater_container);
		// clear the tear-off host right after, the "last host used" will still stick
		setHost(NULL);
		// reparent to floater view
		gFloaterView->addChild(this);
	}
	else
	{
		floater_container->addFloater(this, TRUE);
	}

	openTab(key.asString());

	LLFloater::onOpen(key);
}

void FSFloaterContacts::openTab(const std::string& name)
{
	if (name == "friends")
	{
		childShowTab("friends_and_groups", "friends_panel");
	}
	else if (name == "groups")
	{
		childShowTab("friends_and_groups", "groups_panel");
		updateGroupButtons();
	}
	else if (name == "contact_sets")
	{
		childShowTab("friends_and_groups", "contact_sets_panel");
	}
	else
	{
		return;
	}

	FSFloaterIMContainer* floater_container = dynamic_cast<FSFloaterIMContainer*>(getHost());
	if (floater_container)
	{
		floater_container->setVisible(TRUE);
		floater_container->showFloater(this);
	}
	else
	{
		setVisible(TRUE);
		setFrontmost();
	}
}

//static
FSFloaterContacts* FSFloaterContacts::findInstance()
{
	return LLFloaterReg::findTypedInstance<FSFloaterContacts>("imcontacts");
}

//static
FSFloaterContacts* FSFloaterContacts::getInstance()
{
	return LLFloaterReg::getTypedInstance<FSFloaterContacts>("imcontacts");
}


//
// Friend actions
//

void FSFloaterContacts::onImButtonClicked()
{
	uuid_vec_t selected_uuids;
	getCurrentItemIDs(selected_uuids);
	if (selected_uuids.size() == 1)
	{
		// if selected only one person then start up IM
		LLAvatarActions::startIM(selected_uuids.front());
	}
	else if (selected_uuids.size() > 1)
	{
		// for multiple selection start up friends conference
		LLAvatarActions::startConference(selected_uuids);
	}
}

void FSFloaterContacts::onViewProfileButtonClicked()
{
	LLUUID id = getCurrentItemID();
	LLAvatarActions::showProfile(id);
}

void FSFloaterContacts::onTeleportButtonClicked()
{
	uuid_vec_t selected_uuids;
	getCurrentItemIDs(selected_uuids);
	LLAvatarActions::offerTeleport(selected_uuids);
}

void FSFloaterContacts::onPayButtonClicked()
{
	LLUUID id = getCurrentItemID();
	if (id.notNull())
	{
		LLAvatarActions::pay(id);
	}
}

void FSFloaterContacts::onDeleteFriendButtonClicked()
{
	uuid_vec_t selected_uuids;
	getCurrentItemIDs(selected_uuids);

	if (selected_uuids.size() == 1)
	{
		LLAvatarActions::removeFriendDialog(selected_uuids.front());
	}
	else if (selected_uuids.size() > 1)
	{
		LLAvatarActions::removeFriendsDialog(selected_uuids);
	}
}

bool FSFloaterContacts::isItemsFreeOfFriends(const uuid_vec_t& uuids)
{
	const LLAvatarTracker& av_tracker = LLAvatarTracker::instance();
	for ( uuid_vec_t::const_iterator
			  id = uuids.begin(),
			  id_end = uuids.end();
		  id != id_end; ++id )
	{
		if (av_tracker.isBuddy (*id))
		{
			return false;
		}
	}
	return true;
}

// static
void FSFloaterContacts::onAvatarPicked(const uuid_vec_t& ids, const std::vector<LLAvatarName> names)
{
	if (!names.empty() && !ids.empty())
	{
		LLAvatarActions::requestFriendshipDialog(ids[0], names[0].getCompleteName());
	}
}

void FSFloaterContacts::onAddFriendWizButtonClicked(LLUICtrl* ctrl)
{
	// Show add friend wizard.
	LLFloaterAvatarPicker* picker = LLFloaterAvatarPicker::show(boost::bind(&FSFloaterContacts::onAvatarPicked, _1, _2), FALSE, TRUE, TRUE, "", ctrl);
	// Need to disable 'ok' button when friend occurs in selection
	if (picker)
	{
		picker->setOkBtnEnableCb(boost::bind(&FSFloaterContacts::isItemsFreeOfFriends, this, _1));
	}

	LLFloater* root_floater = gFloaterView->getParentFloater(this);
	if (root_floater)
	{
		root_floater->addDependentFloater(picker);
	}
}

//
// Group actions
//

void FSFloaterContacts::onGroupChatButtonClicked()
{
	LLUUID group_id = getCurrentItemID();
	if (group_id.notNull())
	{
		LLGroupActions::startIM(group_id);
	}
}

void FSFloaterContacts::onGroupInfoButtonClicked()
{
	LLGroupActions::show(getCurrentItemID());
}

void FSFloaterContacts::onGroupActivateButtonClicked()
{
	LLGroupActions::activate(mGroupList->getSelectedUUID());
}

void FSFloaterContacts::onGroupLeaveButtonClicked()
{
	LLUUID group_id = getCurrentItemID();
	if (group_id.notNull())
	{
		LLGroupActions::leave(group_id);
	}
}

void FSFloaterContacts::onGroupCreateButtonClicked()
{
	LLGroupActions::createGroup();
}

void FSFloaterContacts::onGroupSearchButtonClicked()
{
	LLGroupActions::search();
}

void FSFloaterContacts::onGroupTitlesButtonClicked()
{
	LLFloaterReg::toggleInstance("fs_group_titles");
}

void FSFloaterContacts::onGroupInviteButtonClicked()
{
	LLUUID group_id = getCurrentItemID();
	if (group_id.notNull())
	{
		LLFloaterGroupInvite::showForGroup(group_id);
	}
}

//
// Tab and list functions
//

std::string FSFloaterContacts::getActiveTabName() const
{
	return mTabContainer->getCurrentPanel()->getName();
}

LLPanel* FSFloaterContacts::getPanelByName(const std::string& panel_name)
{
	return mTabContainer->getPanelByName(panel_name);
}

LLUUID FSFloaterContacts::getCurrentItemID() const
{
	std::string cur_tab = getActiveTabName();

	if (cur_tab == FRIENDS_TAB_NAME)
	{
		LLScrollListItem* selected = mFriendsList->getFirstSelected();
		if (selected)
		{
			return selected->getUUID();
		}
		else
		{
			return LLUUID::null;
		}
	}
	else if (cur_tab == GROUP_TAB_NAME)
	{
		return mGroupList->getSelectedUUID();
	}

	return LLUUID::null;
}

void FSFloaterContacts::getCurrentItemIDs(uuid_vec_t& selected_uuids) const
{
	std::string cur_tab = getActiveTabName();

	if (cur_tab == FRIENDS_TAB_NAME)
	{
		getCurrentFriendItemIDs(selected_uuids);
	}
	else if (cur_tab == GROUP_TAB_NAME)
	{
		mGroupList->getSelectedUUIDs(selected_uuids);
	}
}

void FSFloaterContacts::getCurrentFriendItemIDs(uuid_vec_t& selected_uuids) const
{
	listitem_vec_t selected = mFriendsList->getAllSelected();
	for (listitem_vec_t::iterator itr = selected.begin(); itr != selected.end(); ++itr)
	{
		selected_uuids.push_back((*itr)->getUUID());
	}
}

void FSFloaterContacts::sortFriendList()
{
	mFriendsList->refreshLineHeight();
	mFriendsList->clearSortOrder();

	if (gSavedSettings.getS32("FSFriendListSortOrder"))
	{
		mFriendsList->getColumn(LIST_FRIEND_USER_NAME)->mSortingColumn = mFriendsList->getColumn(LIST_FRIEND_DISPLAY_NAME)->mName;
		mFriendsList->getColumn(LIST_FRIEND_DISPLAY_NAME)->mSortingColumn = mFriendsList->getColumn(LIST_FRIEND_DISPLAY_NAME)->mName;
		mFriendsList->getColumn(LIST_FRIEND_NAME)->mSortingColumn = mFriendsList->getColumn(LIST_FRIEND_DISPLAY_NAME)->mName;
		mFriendsList->sortByColumn(std::string("display_name"), TRUE);
	}
	else
	{
		mFriendsList->getColumn(LIST_FRIEND_USER_NAME)->mSortingColumn = mFriendsList->getColumn(LIST_FRIEND_USER_NAME)->mName;
		mFriendsList->getColumn(LIST_FRIEND_DISPLAY_NAME)->mSortingColumn = mFriendsList->getColumn(LIST_FRIEND_USER_NAME)->mName;
		mFriendsList->getColumn(LIST_FRIEND_NAME)->mSortingColumn = mFriendsList->getColumn(LIST_FRIEND_USER_NAME)->mName;
		mFriendsList->sortByColumn(std::string("user_name"), TRUE);
	}
	mFriendsList->sortByColumn(std::string("icon_online_status"), FALSE);
}


//
// Friends list
//

void FSFloaterContacts::changed(U32 changed_mask)
{
	LLAvatarTracker& at = LLAvatarTracker::instance();

	switch (changed_mask)
	{
		case (LLFriendObserver::ADD | LLFriendObserver::ONLINE) :
			{
				// MAINT-5250 will cause this kind of mask being sent at login
				LLAvatarTracker::buddy_map_t all_buddies;
				LLAvatarTracker::buddy_map_t::const_iterator buddies_iter;
				at.copyBuddyList(all_buddies);
			
				for (buddies_iter = all_buddies.begin(); buddies_iter != all_buddies.end(); ++buddies_iter)
				{
					addFriend(buddies_iter->first);
					if (at.isBuddyOnline(buddies_iter->first))
					{
						const LLRelationship* info = at.getBuddyInfo(buddies_iter->first);
						updateFriendItem(buddies_iter->first, info);
					}
				}
			}
			break;
		case LLFriendObserver::ADD:
			{
				const std::set<LLUUID>& changed_items = at.getChangedIDs();
				std::set<LLUUID>::const_iterator id_it = changed_items.begin();
				std::set<LLUUID>::const_iterator id_end = changed_items.end();
				for (;id_it != id_end; ++id_it)
				{
					addFriend(*id_it);
				}
			}
			break;
		case LLFriendObserver::REMOVE:
			{
				const std::set<LLUUID>& changed_items = at.getChangedIDs();
				std::set<LLUUID>::const_iterator id_it = changed_items.begin();
				std::set<LLUUID>::const_iterator id_end = changed_items.end();
				for (;id_it != id_end; ++id_it)
				{
					mFriendsList->deleteSingleItem(mFriendsList->getItemIndex(*id_it));
				}
			}
			break;
		case LLFriendObserver::POWERS:
			{
				--mNumRightsChanged;
				if (mNumRightsChanged > 0)
				{
					mAllowRightsChange = false;
				}
				else
				{
					mAllowRightsChange = true;
				}
			
				const std::set<LLUUID>& changed_items = at.getChangedIDs();
				std::set<LLUUID>::const_iterator id_it = changed_items.begin();
				std::set<LLUUID>::const_iterator id_end = changed_items.end();
				for (;id_it != id_end; ++id_it)
				{
					const LLRelationship* info = at.getBuddyInfo(*id_it);
					updateFriendItem(*id_it, info);
				}
			}
			break;
		case LLFriendObserver::ONLINE:
			{
				const std::set<LLUUID>& changed_items = at.getChangedIDs();
				std::set<LLUUID>::const_iterator id_it = changed_items.begin();
				std::set<LLUUID>::const_iterator id_end = changed_items.end();
				for (;id_it != id_end; ++id_it)
				{
					const LLRelationship* info = at.getBuddyInfo(*id_it);
					updateFriendItem(*id_it, info);
				}
			}
		default:;
	}
	
	refreshUI();
}

void FSFloaterContacts::addFriend(const LLUUID& agent_id)
{
	LLAvatarTracker& at = LLAvatarTracker::instance();
	const LLRelationship* relationInfo = at.getBuddyInfo(agent_id);
	
	if (!relationInfo)
	{
		return;
	}

	LLAvatarName av_name;
	if (!LLAvatarNameCache::get(agent_id, &av_name))
	{
		const LLRelationship* info = at.getBuddyInfo(agent_id);
		LLUUID request_id = LLUUID::generateNewID();
		LLAvatarNameCache::callback_connection_t conn = LLAvatarNameCache::get(agent_id, boost::bind(&FSFloaterContacts::updateFriendItem, this, agent_id, info, request_id));
		mAvatarNameCacheConnections[request_id] = conn;
	}

	LLSD element;
	element["id"] = agent_id;
	LLSD& username_column				= element["columns"][LIST_FRIEND_USER_NAME];
	username_column["column"]			= "user_name";
	username_column["value"]			= av_name.getUserNameForDisplay();
	username_column["font"]["name"]		= mFriendListFontName;
	username_column["font"]["style"]	= "NORMAL";
	
	LLSD& display_name_column			= element["columns"][LIST_FRIEND_DISPLAY_NAME];
	display_name_column["column"]		= "display_name";
	display_name_column["value"]		= av_name.getDisplayName();
	display_name_column["font"]["name"]	= mFriendListFontName;
	display_name_column["font"]["style"]= "NORMAL";

	LLSD& friend_column					= element["columns"][LIST_FRIEND_NAME];
	friend_column["column"]				= "full_name";
	friend_column["value"]				= getFullName(av_name);
	friend_column["font"]["name"]		= mFriendListFontName;
	friend_column["font"]["style"]		= "NORMAL";

	LLSD& online_status_column			= element["columns"][LIST_ONLINE_STATUS];
	online_status_column["column"]		= "icon_online_status";
	online_status_column["type"]		= "icon";
	online_status_column["halign"]		= "center";

	LLSD& online_column						= element["columns"][LIST_VISIBLE_ONLINE];
	online_column["column"]					= "icon_visible_online";
	online_column["type"]					= "checkbox";
	online_column["value"]					= relationInfo->isRightGrantedTo(LLRelationship::GRANT_ONLINE_STATUS);

	LLSD& visible_map_column				= element["columns"][LIST_VISIBLE_MAP];
	visible_map_column["column"]			= "icon_visible_map";
	visible_map_column["type"]				= "checkbox";
	visible_map_column["value"]				= relationInfo->isRightGrantedTo(LLRelationship::GRANT_MAP_LOCATION);

	LLSD& edit_my_object_column				= element["columns"][LIST_EDIT_MINE];
	edit_my_object_column["column"]			= "icon_edit_mine";
	edit_my_object_column["type"]			= "checkbox";
	edit_my_object_column["value"]			= relationInfo->isRightGrantedTo(LLRelationship::GRANT_MODIFY_OBJECTS);

	LLSD& visible_their_map_column			= element["columns"][LIST_VISIBLE_MAP_THEIRS];
	visible_their_map_column["column"]		= "icon_visible_map_theirs";
	visible_their_map_column["type"]		= "checkbox";
	visible_their_map_column["enabled"]		= "";
	visible_their_map_column["value"]		= relationInfo->isRightGrantedFrom(LLRelationship::GRANT_MAP_LOCATION);
	
	LLSD& edit_their_object_column			= element["columns"][LIST_EDIT_THEIRS];
	edit_their_object_column["column"]		= "icon_edit_theirs";
	edit_their_object_column["type"]		= "checkbox";
	edit_their_object_column["enabled"]		= "";
	edit_their_object_column["value"]		= relationInfo->isRightGrantedFrom(LLRelationship::GRANT_MODIFY_OBJECTS);

	LLSD& update_gen_column					= element["columns"][LIST_FRIEND_UPDATE_GEN];
	update_gen_column["column"]				= "friend_last_update_generation";
	update_gen_column["value"]				= relationInfo->getChangeSerialNum();

	mFriendsList->addElement(element, ADD_BOTTOM);
}

void FSFloaterContacts::onMapButtonClicked()
{
	LLUUID current_id = getCurrentItemID();
	if (current_id.notNull() && is_agent_mappable(current_id))
	{
		LLAvatarActions::showOnMap(current_id);
	}
}

// propagate actual relationship to UI.
// Does not resort the UI list because it can be called frequently. JC
void FSFloaterContacts::updateFriendItem(const LLUUID& agent_id, const LLRelationship* info)
{
	if (!info)
	{
		return;
	}

	LLScrollListItem* itemp = mFriendsList->getItem(agent_id);
	if (!itemp)
	{
		return;
	}
	
	bool isOnlineSIP = LLVoiceClient::getInstance()->isOnlineSIP(itemp->getUUID());
	bool isOnline = info->isOnline();

	LLAvatarName av_name;
	if (!LLAvatarNameCache::get(agent_id, &av_name))
	{
		LLUUID request_id = LLUUID::generateNewID();
		LLAvatarNameCache::callback_connection_t conn = LLAvatarNameCache::get(agent_id, boost::bind(&FSFloaterContacts::updateFriendItem, this, agent_id, info, request_id));
		mAvatarNameCacheConnections[request_id] = conn;
	}

	// Name of the status icon to use
	std::string statusIcon;
	if (isOnline)
	{
		statusIcon = "icon_avatar_online";
	}
	else if(isOnlineSIP)
	{
		statusIcon = "slim_icon_16_viewer";
	}

	itemp->getColumn(LIST_ONLINE_STATUS)->setValue(statusIcon);

	itemp->getColumn(LIST_FRIEND_USER_NAME)->setValue(av_name.getUserNameForDisplay());
	itemp->getColumn(LIST_FRIEND_DISPLAY_NAME)->setValue(av_name.getDisplayName());
	itemp->getColumn(LIST_FRIEND_NAME)->setValue(getFullName(av_name));

	// render name of online friends in bold text
	LLFontGL::StyleFlags font_style = ((isOnline || isOnlineSIP) ? LLFontGL::BOLD : LLFontGL::NORMAL);
	((LLScrollListText*)itemp->getColumn(LIST_FRIEND_USER_NAME))->setFontStyle(font_style);
	((LLScrollListText*)itemp->getColumn(LIST_FRIEND_DISPLAY_NAME))->setFontStyle(font_style);
	((LLScrollListText*)itemp->getColumn(LIST_FRIEND_NAME))->setFontStyle(font_style);

	itemp->getColumn(LIST_VISIBLE_ONLINE)->setValue(info->isRightGrantedTo(LLRelationship::GRANT_ONLINE_STATUS));
	itemp->getColumn(LIST_VISIBLE_MAP)->setValue(info->isRightGrantedTo(LLRelationship::GRANT_MAP_LOCATION));
	itemp->getColumn(LIST_EDIT_MINE)->setValue(info->isRightGrantedTo(LLRelationship::GRANT_MODIFY_OBJECTS));
	itemp->getColumn(LIST_VISIBLE_MAP_THEIRS)->setValue(info->isRightGrantedFrom(LLRelationship::GRANT_MAP_LOCATION));
	itemp->getColumn(LIST_EDIT_THEIRS)->setValue(info->isRightGrantedFrom(LLRelationship::GRANT_MODIFY_OBJECTS));
	S32 change_generation = info->getChangeSerialNum();
	itemp->getColumn(LIST_FRIEND_UPDATE_GEN)->setValue(change_generation);

	// enable this item, in case it was disabled after user input
	itemp->setEnabled(TRUE);
}

void FSFloaterContacts::updateFriendItem(const LLUUID& agent_id, const LLRelationship* relationship, const LLUUID& request_id)
{
	disconnectAvatarNameCacheConnection(request_id);
	updateFriendItem(agent_id, relationship);
}

void FSFloaterContacts::refreshRightsChangeList()
{
	uuid_vec_t friends;
	getCurrentFriendItemIDs(friends);

	size_t num_selected = friends.size();

	bool can_offer_teleport = num_selected >= 1;
	bool selected_friends_online = true;

	const LLRelationship* friend_status = NULL;
	for (uuid_vec_t::iterator itr = friends.begin(); itr != friends.end(); ++itr)
	{
		friend_status = LLAvatarTracker::instance().getBuddyInfo(*itr);
		if (friend_status)
		{
			if (!friend_status->isOnline())
			{
				can_offer_teleport = false;
				selected_friends_online = false;
			}
			else
			{
				if (gRlvHandler.hasBehaviour(RLV_BHVR_SHOWLOC) &&
					!gRlvHandler.isException(RLV_BHVR_TPLURE, *itr, ERlvExceptionCheck::Permissive) &&
					!friend_status->isRightGrantedTo(LLRelationship::GRANT_MAP_LOCATION))
				{
					can_offer_teleport = false;
				}
			}
		}
		else // missing buddy info, don't allow any operations
		{
			can_offer_teleport = false;
		}
	}
	
	if (num_selected == 0) // nothing selected
	{
		childSetEnabled("im_btn", false);
		childSetEnabled("offer_teleport_btn", false);
	}
	else // we have at least one friend selected...
	{
		// only allow IMs to groups when everyone in the group is online
		// to be consistent with context menus in inventory and because otherwise
		// offline friends would be silently dropped from the session
		childSetEnabled("im_btn", selected_friends_online || num_selected == 1);
		childSetEnabled("offer_teleport_btn", can_offer_teleport);
	}
}

void FSFloaterContacts::refreshUI()
{
	mFriendsList->refreshLineHeight();
	mFriendsList->setNeedsSort(true);

	bool single_selected = false;
	bool multiple_selected = false;
	size_t num_selected = mFriendsList->getAllSelected().size();
	if (num_selected > 0)
	{
		single_selected = true;
		multiple_selected = (num_selected > 1);
	}

	//Options that can only be performed with one friend selected
	childSetEnabled("profile_btn", single_selected && !multiple_selected);

	//Options that can be performed with up to MAX_FRIEND_SELECT friends selected
	//(single_selected will always be true in this situations)
	childSetEnabled("remove_btn", single_selected);
	childSetEnabled("im_btn", single_selected);

	LLScrollListItem* selected_item = mFriendsList->getFirstSelected();
	bool mappable = false;
	bool payable = false;
	if (selected_item)
	{
		LLUUID av_id = selected_item->getUUID();
		mappable = (single_selected && !multiple_selected && av_id.notNull() && is_agent_mappable(av_id));
		payable = (single_selected && !multiple_selected && av_id.notNull() && RlvActions::canPayAvatar(av_id));
	}
	childSetEnabled("map_btn", mappable && !gRlvHandler.hasBehaviour(RLV_BHVR_SHOWWORLDMAP));
	childSetEnabled("pay_btn", payable);

	// Set friend count
	mFriendsTab->getChild<LLTextBox>("friend_count")->setTextArg("COUNT", llformat("%d", mFriendsList->getItemCount()));

	refreshRightsChangeList();
}

void FSFloaterContacts::onSelectName()
{
	refreshUI();
	// check to see if rights have changed
	applyRightsToFriends();
}

void FSFloaterContacts::confirmModifyRights(rights_map_t& ids, EGrantRevoke command)
{
	if (ids.empty())
	{
		return;
	}
	
	LLSD args;
	if (ids.size() > 0)
	{
		rights_map_t* rights = new rights_map_t(ids);

		// for single friend, show their name
		if (ids.size() == 1)
		{
			args["NAME"] = LLSLURL("agent", ids.begin()->first, "completename").getSLURLString();
			if (command == GRANT)
			{
				LLNotificationsUtil::add("GrantModifyRights", 
					args, 
					LLSD(), 
					boost::bind(&FSFloaterContacts::modifyRightsConfirmation, this, _1, _2, rights));
			}
			else
			{
				LLNotificationsUtil::add("RevokeModifyRights", 
					args, 
					LLSD(), 
					boost::bind(&FSFloaterContacts::modifyRightsConfirmation, this, _1, _2, rights));
			}
		}
		else
		{
			if (command == GRANT)
			{
				LLNotificationsUtil::add("GrantModifyRightsMultiple", 
					args, 
					LLSD(), 
					boost::bind(&FSFloaterContacts::modifyRightsConfirmation, this, _1, _2, rights));
			}
			else
			{
				LLNotificationsUtil::add("RevokeModifyRightsMultiple", 
					args, 
					LLSD(), 
					boost::bind(&FSFloaterContacts::modifyRightsConfirmation, this, _1, _2, rights));
			}
		}
	}
	
}

bool FSFloaterContacts::modifyRightsConfirmation(const LLSD& notification, const LLSD& response, rights_map_t* rights)
{
	mRightsChangeNotificationTriggered = false;

	S32 option = LLNotificationsUtil::getSelectedOption(notification, response);
	if (0 == option)
	{
		sendRightsGrant(*rights);
	}
	else
	{
		// need to resync view with model, since user cancelled operation
		rights_map_t::iterator rights_it;
		for (rights_it = rights->begin(); rights_it != rights->end(); ++rights_it)
		{
			const LLRelationship* info = LLAvatarTracker::instance().getBuddyInfo(rights_it->first);
			updateFriendItem(rights_it->first, info);
		}
	}
	refreshUI();

	delete rights;
	return false;
}

void FSFloaterContacts::applyRightsToFriends()
{
	if (mRightsChangeNotificationTriggered)
	{
		return;
	}

	bool rights_changed = false;

	// store modify rights separately for confirmation
	rights_map_t rights_updates;

	bool need_confirmation = false;
	EGrantRevoke confirmation_type = GRANT;

	// this assumes that changes only happened to selected items
	listitem_vec_t selected = mFriendsList->getAllSelected();
	for (listitem_vec_t::iterator itr = selected.begin(); itr != selected.end(); ++itr)
	{
		LLUUID id = (*itr)->getValue();
		const LLRelationship* buddy_relationship = LLAvatarTracker::instance().getBuddyInfo(id);
		if (buddy_relationship == NULL)
		{
			continue;
		}

		bool show_online_staus = (*itr)->getColumn(LIST_VISIBLE_ONLINE)->getValue().asBoolean();
		bool show_map_location = (*itr)->getColumn(LIST_VISIBLE_MAP)->getValue().asBoolean();
		bool allow_modify_objects = (*itr)->getColumn(LIST_EDIT_MINE)->getValue().asBoolean();

		S32 rights = buddy_relationship->getRightsGrantedTo();
		if (buddy_relationship->isRightGrantedTo(LLRelationship::GRANT_ONLINE_STATUS) != show_online_staus)
		{
			rights_changed = true;
			if (show_online_staus) 
			{
				rights |= LLRelationship::GRANT_ONLINE_STATUS;
			}
			else 
			{
				// ONLINE_STATUS necessary for MAP_LOCATION
				rights &= ~LLRelationship::GRANT_ONLINE_STATUS;
				rights &= ~LLRelationship::GRANT_MAP_LOCATION;
				// propagate rights constraint to UI
				(*itr)->getColumn(LIST_VISIBLE_MAP)->setValue(FALSE);
			}
		}
		if (buddy_relationship->isRightGrantedTo(LLRelationship::GRANT_MAP_LOCATION) != show_map_location)
		{
			rights_changed = true;
			if (show_map_location) 
			{
				// ONLINE_STATUS necessary for MAP_LOCATION
				rights |= LLRelationship::GRANT_MAP_LOCATION;
				rights |= LLRelationship::GRANT_ONLINE_STATUS;
				(*itr)->getColumn(LIST_VISIBLE_ONLINE)->setValue(TRUE);
			}
			else 
			{
				rights &= ~LLRelationship::GRANT_MAP_LOCATION;
			}
		}
		
		// now check for change in modify object rights, which requires confirmation
		if (buddy_relationship->isRightGrantedTo(LLRelationship::GRANT_MODIFY_OBJECTS) != allow_modify_objects)
		{
			rights_changed = true;
			need_confirmation = true;

			if (allow_modify_objects)
			{
				rights |= LLRelationship::GRANT_MODIFY_OBJECTS;
				confirmation_type = GRANT;
			}
			else
			{
				rights &= ~LLRelationship::GRANT_MODIFY_OBJECTS;
				confirmation_type = REVOKE;
			}
		}

		if (rights_changed)
		{
			rights_updates.insert(std::make_pair(id, rights));
			// disable these ui elements until response from server
			// to avoid race conditions
			(*itr)->setEnabled(FALSE);
		}
	}

	// separately confirm grant and revoke of modify rights
	if (need_confirmation)
	{
		confirmModifyRights(rights_updates, confirmation_type);
		mRightsChangeNotificationTriggered = true;
	}
	else
	{
		sendRightsGrant(rights_updates);
	}
}

void FSFloaterContacts::sendRightsGrant(rights_map_t& ids)
{
	if (ids.empty())
	{
		return;
	}

	LLMessageSystem* msg = gMessageSystem;

	// setup message header
	msg->newMessageFast(_PREHASH_GrantUserRights);
	msg->nextBlockFast(_PREHASH_AgentData);
	msg->addUUID(_PREHASH_AgentID, gAgent.getID());
	msg->addUUID(_PREHASH_SessionID, gAgent.getSessionID());

	rights_map_t::iterator id_it;
	rights_map_t::iterator end_it = ids.end();
	for (id_it = ids.begin(); id_it != end_it; ++id_it)
	{
		msg->nextBlockFast(_PREHASH_Rights);
		msg->addUUID(_PREHASH_AgentRelated, id_it->first);
		msg->addS32(_PREHASH_RelatedRights, id_it->second);
	}

	mNumRightsChanged = ids.size();
	gAgent.sendReliableMessage();
}

void FSFloaterContacts::childShowTab(const std::string& id, const std::string& tabname)
{
	LLTabContainer* child = findChild<LLTabContainer>(id);
	if (child)
	{
		child->selectTabByName(tabname);
	}
}

void FSFloaterContacts::updateRlvRestrictions(ERlvBehaviour behavior)
{
	if (behavior == RLV_BHVR_SHOWLOC ||
		behavior == RLV_BHVR_SHOWWORLDMAP ||
		behavior == RLV_BHVR_PAY)
	{
		refreshUI();
	}
}

void FSFloaterContacts::onColumnDisplayModeChanged(const std::string& settings_name)
{
	mLastColumnDisplayModeChanged = settings_name;

	// Make sure at least one column is shown!
	if (!settings_name.empty() &&
		!gSavedSettings.getBOOL("FSFriendListColumnShowUserName") &&
		!gSavedSettings.getBOOL("FSFriendListColumnShowDisplayName") &&
		!gSavedSettings.getBOOL("FSFriendListColumnShowFullName"))
	{
		mResetLastColumnDisplayModeChanged = true;
		return;
	}

	std::vector<LLScrollListColumn::Params> column_params = mFriendsList->getColumnInitParams();

	mFriendsList->clearColumns();
	mFriendsList->updateLayout();

	for (std::vector<LLScrollListColumn::Params>::iterator it = column_params.begin(); it != column_params.end(); ++it)
	{
		LLScrollListColumn::Params p = *it;

		if (p.name.getValue() == "user_name")
		{
			LLScrollListColumn::Params params;
			params.header = p.header;
			params.name = p.name;
			params.halign = p.halign;
			params.sort_direction = p.sort_direction;
			params.sort_column = p.sort_column;
			params.tool_tip = p.tool_tip;

			if (gSavedSettings.getBOOL("FSFriendListColumnShowUserName"))
			{
				params.width.dynamic_width.set(true, true);
			}
			else
			{
				params.width.pixel_width.set(-1, true);
			}
			mFriendsList->addColumn(params);
		}
		else if (p.name.getValue() == "display_name")
		{
			LLScrollListColumn::Params params;
			params.header = p.header;
			params.name = p.name;
			params.halign = p.halign;
			params.sort_direction = p.sort_direction;
			params.sort_column = p.sort_column;
			params.tool_tip = p.tool_tip;

			if (gSavedSettings.getBOOL("FSFriendListColumnShowDisplayName"))
			{
				params.width.dynamic_width.set(true, true);
			}
			else
			{
				params.width.pixel_width.set(-1, true);
			}
			mFriendsList->addColumn(params);
		}
		else if (p.name.getValue() == "full_name")
		{
			LLScrollListColumn::Params params;
			params.header = p.header;
			params.name = p.name;
			params.halign = p.halign;
			params.sort_direction = p.sort_direction;
			params.sort_column = p.sort_column;
			params.tool_tip = p.tool_tip;

			if (gSavedSettings.getBOOL("FSFriendListColumnShowFullName"))
			{
				params.width.dynamic_width.set(true, true);
			}
			else
			{
				params.width.pixel_width.set(-1, true);
			}
			mFriendsList->addColumn(params);
		}
		else if (p.name.getValue() == "icon_visible_online" ||
			p.name.getValue() == "icon_visible_map" ||
			p.name.getValue() == "icon_edit_mine" ||
			p.name.getValue() == "icon_visible_map_theirs" ||
			p.name.getValue() == "icon_edit_theirs")
		{
			LLScrollListColumn::Params params;
			params.header = p.header;
			params.name = p.name;
			params.halign = p.halign;
			params.sort_direction = p.sort_direction;
			params.sort_column = p.sort_column;
			params.tool_tip = p.tool_tip;

			if (gSavedSettings.getBOOL("FSFriendListColumnShowPermissions"))
			{
				params.width = p.width;
			}
			else
			{
				params.width.pixel_width.set(-1, true);
			}
			mFriendsList->addColumn(params);
		}
		else
		{
			mFriendsList->addColumn(p);
		}
		
	}

	mFriendsList->dirtyColumns();
	mFriendsList->updateColumns(true); // Force update or icons don't properly hide if permission columns are hidden

	// primary sort = online status, secondary sort = name
	if (gSavedSettings.getS32("FSFriendListSortOrder"))
	{
		mFriendsList->getColumn(LIST_FRIEND_USER_NAME)->mSortingColumn = mFriendsList->getColumn(LIST_FRIEND_DISPLAY_NAME)->mName;
		mFriendsList->getColumn(LIST_FRIEND_DISPLAY_NAME)->mSortingColumn = mFriendsList->getColumn(LIST_FRIEND_DISPLAY_NAME)->mName;
		mFriendsList->getColumn(LIST_FRIEND_NAME)->mSortingColumn = mFriendsList->getColumn(LIST_FRIEND_DISPLAY_NAME)->mName;
		mFriendsList->sortByColumn(std::string("display_name"), TRUE);
	}
	else
	{
		mFriendsList->getColumn(LIST_FRIEND_USER_NAME)->mSortingColumn = mFriendsList->getColumn(LIST_FRIEND_USER_NAME)->mName;
		mFriendsList->getColumn(LIST_FRIEND_DISPLAY_NAME)->mSortingColumn = mFriendsList->getColumn(LIST_FRIEND_USER_NAME)->mName;
		mFriendsList->getColumn(LIST_FRIEND_NAME)->mSortingColumn = mFriendsList->getColumn(LIST_FRIEND_USER_NAME)->mName;
		mFriendsList->sortByColumn(std::string("user_name"), TRUE);
	}
	mFriendsList->sortByColumn(std::string("icon_online_status"), FALSE);
	mFriendsList->setSearchColumn(mFriendsList->getColumn("full_name")->mIndex);
}

void FSFloaterContacts::onDisplayNameChanged()
{
	listitem_vec_t items = mFriendsList->getAllData();
	for (listitem_vec_t::iterator it = items.begin(); it != items.end(); ++it)
	{
		LLAvatarName av_name;
		if (LLAvatarNameCache::get((*it)->getUUID(), &av_name))
		{
			(*it)->getColumn(LIST_FRIEND_USER_NAME)->setValue(av_name.getUserNameForDisplay());
			(*it)->getColumn(LIST_FRIEND_DISPLAY_NAME)->setValue(av_name.getDisplayName());
			(*it)->getColumn(LIST_FRIEND_NAME)->setValue(getFullName(av_name));
		}
		else
		{
			LLUUID request_id = LLUUID::generateNewID();
			LLAvatarNameCache::callback_connection_t conn = LLAvatarNameCache::get((*it)->getUUID(), boost::bind(&FSFloaterContacts::setDirtyNames, this, request_id));
			mAvatarNameCacheConnections[request_id] = conn;
		}
	}
	mFriendsList->setNeedsSort();
}

std::string FSFloaterContacts::getFullName(const LLAvatarName& av_name)
{
	if (av_name.isDisplayNameDefault() || !gSavedSettings.getBOOL("UseDisplayNames"))
	{
		return av_name.getUserNameForDisplay();
	}

	if (gSavedSettings.getS32("FSFriendListFullNameFormat"))
	{
		// Display name (Username)
		return llformat("%s (%s)", av_name.getDisplayName().c_str(), av_name.getUserNameForDisplay().c_str());
	}
	else
	{
		// Username (Display name)
		return llformat("%s (%s)", av_name.getUserNameForDisplay().c_str(), av_name.getDisplayName().c_str());
	}
}

void FSFloaterContacts::setDirtyNames(const LLUUID& request_id)
{
	disconnectAvatarNameCacheConnection(request_id);
	mDirtyNames = true;
}

void FSFloaterContacts::disconnectAvatarNameCacheConnection(const LLUUID& request_id)
{
	avatar_name_cb_t::iterator found = mAvatarNameCacheConnections.find(request_id);
	if (found != mAvatarNameCacheConnections.end())
	{
		LLAvatarNameCache::callback_connection_t& conn = found->second;
		if (conn.connected())
		{
			conn.disconnect();
		}
		mAvatarNameCacheConnections.erase(found);
	}
}

BOOL FSFloaterContacts::handleFriendsListDragAndDrop(S32 x, S32 y, MASK mask, BOOL drop,
														EDragAndDropType cargo_type,
														void* cargo_data,
														EAcceptance* accept,
														std::string& tooltip_msg)
{
	if (cargo_type == DAD_PERSON)
	{
		LLUUID* av_id = static_cast<LLUUID*>(cargo_data);
		if (av_id && !LLAvatarActions::isFriend(*av_id))
		{
			*accept = ACCEPT_YES_SINGLE;

			if (drop)
			{
				LLAvatarActions::requestFriendshipDialog(*av_id);
			}
		}
	}
	else
	{
		LLScrollListItem* hit_item = mFriendsList->hitItem(x, y);
		if (hit_item)
		{
			LLToolDragAndDrop::handleGiveDragAndDrop(hit_item->getUUID(), LLUUID::null, drop,
				cargo_type, cargo_data, accept);
		}
		else
		{
			*accept = ACCEPT_NO;
		}
	}

	return TRUE;
}

void FSFloaterContacts::onFriendFilterEdit(const std::string& search_string)
{
	mFriendFilterSubStringOrig = search_string;
	LLStringUtil::trimHead(mFriendFilterSubStringOrig);
	// Searches are case-insensitive
	std::string search_upper = mFriendFilterSubStringOrig;
	LLStringUtil::toUpper(search_upper);

	if (mFriendFilterSubString == search_upper)
	{
		return;
	}

	mFriendFilterSubString = search_upper;

	// Apply new filter.
	mFriendsList->setFilterString(mFriendFilterSubStringOrig);
}

void FSFloaterContacts::resetFriendFilter()
{
	mFriendFilter->setText(LLStringUtil::null);
	onFriendFilterEdit("");
}

void FSFloaterContacts::onGroupFilterEdit(const std::string& search_string)
{
	mGroupList->setNameFilter(search_string);
}
// EOF
