PageRenderTime 143ms CodeModel.GetById 14ms app.highlight 118ms RepoModel.GetById 1ms app.codeStats 1ms

/indra/newview/llpanelpeople.cpp

https://bitbucket.org/lindenlab/viewer-beta/
C++ | 1553 lines | 1175 code | 237 blank | 141 comment | 238 complexity | 60174ecaf0ae2b5b936878c9359966a3 MD5 | raw file
   1/** 
   2 * @file llpanelpeople.cpp
   3 * @brief Side tray "People" 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// libs
  30#include "llavatarname.h"
  31#include "llfloaterreg.h"
  32#include "llfloatersidepanelcontainer.h"
  33#include "llmenubutton.h"
  34#include "llmenugl.h"
  35#include "llnotificationsutil.h"
  36#include "lleventtimer.h"
  37#include "llfiltereditor.h"
  38#include "lltabcontainer.h"
  39#include "lltoggleablemenu.h"
  40#include "lluictrlfactory.h"
  41
  42#include "llpanelpeople.h"
  43
  44// newview
  45#include "llaccordionctrl.h"
  46#include "llaccordionctrltab.h"
  47#include "llagent.h"
  48#include "llavataractions.h"
  49#include "llavatarlist.h"
  50#include "llavatarlistitem.h"
  51#include "llcallingcard.h"			// for LLAvatarTracker
  52#include "llfloateravatarpicker.h"
  53//#include "llfloaterminiinspector.h"
  54#include "llfriendcard.h"
  55#include "llgroupactions.h"
  56#include "llgrouplist.h"
  57#include "llinventoryobserver.h"
  58#include "llnetmap.h"
  59#include "llpanelpeoplemenus.h"
  60#include "llsidetraypanelcontainer.h"
  61#include "llrecentpeople.h"
  62#include "llviewercontrol.h"		// for gSavedSettings
  63#include "llviewermenu.h"			// for gMenuHolder
  64#include "llvoiceclient.h"
  65#include "llworld.h"
  66#include "llspeakers.h"
  67
  68#define FRIEND_LIST_UPDATE_TIMEOUT	0.5
  69#define NEARBY_LIST_UPDATE_INTERVAL 1
  70
  71static const std::string NEARBY_TAB_NAME	= "nearby_panel";
  72static const std::string FRIENDS_TAB_NAME	= "friends_panel";
  73static const std::string GROUP_TAB_NAME		= "groups_panel";
  74static const std::string RECENT_TAB_NAME	= "recent_panel";
  75
  76static const std::string COLLAPSED_BY_USER  = "collapsed_by_user";
  77
  78/** Comparator for comparing avatar items by last interaction date */
  79class LLAvatarItemRecentComparator : public LLAvatarItemComparator
  80{
  81public:
  82	LLAvatarItemRecentComparator() {};
  83	virtual ~LLAvatarItemRecentComparator() {};
  84
  85protected:
  86	virtual bool doCompare(const LLAvatarListItem* avatar_item1, const LLAvatarListItem* avatar_item2) const
  87	{
  88		LLRecentPeople& people = LLRecentPeople::instance();
  89		const LLDate& date1 = people.getDate(avatar_item1->getAvatarId());
  90		const LLDate& date2 = people.getDate(avatar_item2->getAvatarId());
  91
  92		//older comes first
  93		return date1 > date2;
  94	}
  95};
  96
  97/** Compares avatar items by online status, then by name */
  98class LLAvatarItemStatusComparator : public LLAvatarItemComparator
  99{
 100public:
 101	LLAvatarItemStatusComparator() {};
 102
 103protected:
 104	/**
 105	 * @return true if item1 < item2, false otherwise
 106	 */
 107	virtual bool doCompare(const LLAvatarListItem* item1, const LLAvatarListItem* item2) const
 108	{
 109		LLAvatarTracker& at = LLAvatarTracker::instance();
 110		bool online1 = at.isBuddyOnline(item1->getAvatarId());
 111		bool online2 = at.isBuddyOnline(item2->getAvatarId());
 112
 113		if (online1 == online2)
 114		{
 115			std::string name1 = item1->getAvatarName();
 116			std::string name2 = item2->getAvatarName();
 117
 118			LLStringUtil::toUpper(name1);
 119			LLStringUtil::toUpper(name2);
 120
 121			return name1 < name2;
 122		}
 123		
 124		return online1 > online2; 
 125	}
 126};
 127
 128/** Compares avatar items by distance between you and them */
 129class LLAvatarItemDistanceComparator : public LLAvatarItemComparator
 130{
 131public:
 132	typedef std::map < LLUUID, LLVector3d > id_to_pos_map_t;
 133	LLAvatarItemDistanceComparator() {};
 134
 135	void updateAvatarsPositions(std::vector<LLVector3d>& positions, uuid_vec_t& uuids)
 136	{
 137		std::vector<LLVector3d>::const_iterator
 138			pos_it = positions.begin(),
 139			pos_end = positions.end();
 140
 141		uuid_vec_t::const_iterator
 142			id_it = uuids.begin(),
 143			id_end = uuids.end();
 144
 145		LLAvatarItemDistanceComparator::id_to_pos_map_t pos_map;
 146
 147		mAvatarsPositions.clear();
 148
 149		for (;pos_it != pos_end && id_it != id_end; ++pos_it, ++id_it )
 150		{
 151			mAvatarsPositions[*id_it] = *pos_it;
 152		}
 153	};
 154
 155protected:
 156	virtual bool doCompare(const LLAvatarListItem* item1, const LLAvatarListItem* item2) const
 157	{
 158		const LLVector3d& me_pos = gAgent.getPositionGlobal();
 159		const LLVector3d& item1_pos = mAvatarsPositions.find(item1->getAvatarId())->second;
 160		const LLVector3d& item2_pos = mAvatarsPositions.find(item2->getAvatarId())->second;
 161		
 162		return dist_vec_squared(item1_pos, me_pos) < dist_vec_squared(item2_pos, me_pos);
 163	}
 164private:
 165	id_to_pos_map_t mAvatarsPositions;
 166};
 167
 168/** Comparator for comparing nearby avatar items by last spoken time */
 169class LLAvatarItemRecentSpeakerComparator : public  LLAvatarItemNameComparator
 170{
 171public:
 172	LLAvatarItemRecentSpeakerComparator() {};
 173	virtual ~LLAvatarItemRecentSpeakerComparator() {};
 174
 175protected:
 176	virtual bool doCompare(const LLAvatarListItem* item1, const LLAvatarListItem* item2) const
 177	{
 178		LLPointer<LLSpeaker> lhs = LLActiveSpeakerMgr::instance().findSpeaker(item1->getAvatarId());
 179		LLPointer<LLSpeaker> rhs = LLActiveSpeakerMgr::instance().findSpeaker(item2->getAvatarId());
 180		if ( lhs.notNull() && rhs.notNull() )
 181		{
 182			// Compare by last speaking time
 183			if( lhs->mLastSpokeTime != rhs->mLastSpokeTime )
 184				return ( lhs->mLastSpokeTime > rhs->mLastSpokeTime );
 185		}
 186		else if ( lhs.notNull() )
 187		{
 188			// True if only item1 speaker info available
 189			return true;
 190		}
 191		else if ( rhs.notNull() )
 192		{
 193			// False if only item2 speaker info available
 194			return false;
 195		}
 196		// By default compare by name.
 197		return LLAvatarItemNameComparator::doCompare(item1, item2);
 198	}
 199};
 200
 201static const LLAvatarItemRecentComparator RECENT_COMPARATOR;
 202static const LLAvatarItemStatusComparator STATUS_COMPARATOR;
 203static LLAvatarItemDistanceComparator DISTANCE_COMPARATOR;
 204static const LLAvatarItemRecentSpeakerComparator RECENT_SPEAKER_COMPARATOR;
 205
 206static LLRegisterPanelClassWrapper<LLPanelPeople> t_people("panel_people");
 207
 208//=============================================================================
 209
 210/**
 211 * Updates given list either on regular basis or on external events (up to implementation). 
 212 */
 213class LLPanelPeople::Updater
 214{
 215public:
 216	typedef boost::function<void()> callback_t;
 217	Updater(callback_t cb)
 218	: mCallback(cb)
 219	{
 220	}
 221
 222	virtual ~Updater()
 223	{
 224	}
 225
 226	/**
 227	 * Activate/deactivate updater.
 228	 *
 229	 * This may start/stop regular updates.
 230	 */
 231	virtual void setActive(bool) {}
 232
 233protected:
 234	void update()
 235	{
 236		mCallback();
 237	}
 238
 239	callback_t		mCallback;
 240};
 241
 242/**
 243 * Update buttons on changes in our friend relations (STORM-557).
 244 */
 245class LLButtonsUpdater : public LLPanelPeople::Updater, public LLFriendObserver
 246{
 247public:
 248	LLButtonsUpdater(callback_t cb)
 249	:	LLPanelPeople::Updater(cb)
 250	{
 251		LLAvatarTracker::instance().addObserver(this);
 252	}
 253
 254	~LLButtonsUpdater()
 255	{
 256		LLAvatarTracker::instance().removeObserver(this);
 257	}
 258
 259	/*virtual*/ void changed(U32 mask)
 260	{
 261		(void) mask;
 262		update();
 263	}
 264};
 265
 266class LLAvatarListUpdater : public LLPanelPeople::Updater, public LLEventTimer
 267{
 268public:
 269	LLAvatarListUpdater(callback_t cb, F32 period)
 270	:	LLEventTimer(period),
 271		LLPanelPeople::Updater(cb)
 272	{
 273		mEventTimer.stop();
 274	}
 275
 276	virtual BOOL tick() // from LLEventTimer
 277	{
 278		return FALSE;
 279	}
 280};
 281
 282/**
 283 * Updates the friends list.
 284 * 
 285 * Updates the list on external events which trigger the changed() method. 
 286 */
 287class LLFriendListUpdater : public LLAvatarListUpdater, public LLFriendObserver
 288{
 289	LOG_CLASS(LLFriendListUpdater);
 290	class LLInventoryFriendCardObserver;
 291
 292public: 
 293	friend class LLInventoryFriendCardObserver;
 294	LLFriendListUpdater(callback_t cb)
 295	:	LLAvatarListUpdater(cb, FRIEND_LIST_UPDATE_TIMEOUT)
 296	,	mIsActive(false)
 297	{
 298		LLAvatarTracker::instance().addObserver(this);
 299
 300		// For notification when SIP online status changes.
 301		LLVoiceClient::getInstance()->addObserver(this);
 302		mInvObserver = new LLInventoryFriendCardObserver(this);
 303	}
 304
 305	~LLFriendListUpdater()
 306	{
 307		// will be deleted by ~LLInventoryModel
 308		//delete mInvObserver;
 309		LLVoiceClient::getInstance()->removeObserver(this);
 310		LLAvatarTracker::instance().removeObserver(this);
 311	}
 312
 313	/*virtual*/ void changed(U32 mask)
 314	{
 315		if (mIsActive)
 316		{
 317			// events can arrive quickly in bulk - we need not process EVERY one of them -
 318			// so we wait a short while to let others pile-in, and process them in aggregate.
 319			mEventTimer.start();
 320		}
 321
 322		// save-up all the mask-bits which have come-in
 323		mMask |= mask;
 324	}
 325
 326
 327	/*virtual*/ BOOL tick()
 328	{
 329		if (!mIsActive) return FALSE;
 330
 331		if (mMask & (LLFriendObserver::ADD | LLFriendObserver::REMOVE | LLFriendObserver::ONLINE))
 332		{
 333			update();
 334		}
 335
 336		// Stop updates.
 337		mEventTimer.stop();
 338		mMask = 0;
 339
 340		return FALSE;
 341	}
 342
 343	// virtual
 344	void setActive(bool active)
 345	{
 346		mIsActive = active;
 347		if (active)
 348		{
 349			tick();
 350		}
 351	}
 352
 353private:
 354	U32 mMask;
 355	LLInventoryFriendCardObserver* mInvObserver;
 356	bool mIsActive;
 357
 358	/**
 359	 *	This class is intended for updating Friend List when Inventory Friend Card is added/removed.
 360	 * 
 361	 *	The main usage is when Inventory Friends/All content is added while synchronizing with 
 362	 *		friends list on startup is performed. In this case Friend Panel should be updated when 
 363	 *		missing Inventory Friend Card is created.
 364	 *	*NOTE: updating is fired when Inventory item is added into CallingCards/Friends subfolder.
 365	 *		Otherwise LLFriendObserver functionality is enough to keep Friends Panel synchronized.
 366	 */
 367	class LLInventoryFriendCardObserver : public LLInventoryObserver
 368	{
 369		LOG_CLASS(LLFriendListUpdater::LLInventoryFriendCardObserver);
 370
 371		friend class LLFriendListUpdater;
 372
 373	private:
 374		LLInventoryFriendCardObserver(LLFriendListUpdater* updater) : mUpdater(updater)
 375		{
 376			gInventory.addObserver(this);
 377		}
 378		~LLInventoryFriendCardObserver()
 379		{
 380			gInventory.removeObserver(this);
 381		}
 382		/*virtual*/ void changed(U32 mask)
 383		{
 384			lldebugs << "Inventory changed: " << mask << llendl;
 385
 386			static bool synchronize_friends_folders = true;
 387			if (synchronize_friends_folders)
 388			{
 389				// Checks whether "Friends" and "Friends/All" folders exist in "Calling Cards" folder,
 390				// fetches their contents if needed and synchronizes it with buddies list.
 391				// If the folders are not found they are created.
 392				LLFriendCardsManager::instance().syncFriendCardsFolders();
 393				synchronize_friends_folders = false;
 394			}
 395
 396			// *NOTE: deleting of InventoryItem is performed via moving to Trash. 
 397			// That means LLInventoryObserver::STRUCTURE is present in MASK instead of LLInventoryObserver::REMOVE
 398			if ((CALLINGCARD_ADDED & mask) == CALLINGCARD_ADDED)
 399			{
 400				lldebugs << "Calling card added: count: " << gInventory.getChangedIDs().size() 
 401					<< ", first Inventory ID: "<< (*gInventory.getChangedIDs().begin())
 402					<< llendl;
 403
 404				bool friendFound = false;
 405				std::set<LLUUID> changedIDs = gInventory.getChangedIDs();
 406				for (std::set<LLUUID>::const_iterator it = changedIDs.begin(); it != changedIDs.end(); ++it)
 407				{
 408					if (isDescendentOfInventoryFriends(*it))
 409					{
 410						friendFound = true;
 411						break;
 412					}
 413				}
 414
 415				if (friendFound)
 416				{
 417					lldebugs << "friend found, panel should be updated" << llendl;
 418					mUpdater->changed(LLFriendObserver::ADD);
 419				}
 420			}
 421		}
 422
 423		bool isDescendentOfInventoryFriends(const LLUUID& invItemID)
 424		{
 425			LLViewerInventoryItem * item = gInventory.getItem(invItemID);
 426			if (NULL == item)
 427				return false;
 428
 429			return LLFriendCardsManager::instance().isItemInAnyFriendsList(item);
 430		}
 431		LLFriendListUpdater* mUpdater;
 432
 433		static const U32 CALLINGCARD_ADDED = LLInventoryObserver::ADD | LLInventoryObserver::CALLING_CARD;
 434	};
 435};
 436
 437/**
 438 * Periodically updates the nearby people list while the Nearby tab is active.
 439 * 
 440 * The period is defined by NEARBY_LIST_UPDATE_INTERVAL constant.
 441 */
 442class LLNearbyListUpdater : public LLAvatarListUpdater
 443{
 444	LOG_CLASS(LLNearbyListUpdater);
 445
 446public:
 447	LLNearbyListUpdater(callback_t cb)
 448	:	LLAvatarListUpdater(cb, NEARBY_LIST_UPDATE_INTERVAL)
 449	{
 450		setActive(false);
 451	}
 452
 453	/*virtual*/ void setActive(bool val)
 454	{
 455		if (val)
 456		{
 457			// update immediately and start regular updates
 458			update();
 459			mEventTimer.start(); 
 460		}
 461		else
 462		{
 463			// stop regular updates
 464			mEventTimer.stop();
 465		}
 466	}
 467
 468	/*virtual*/ BOOL tick()
 469	{
 470		update();
 471		return FALSE;
 472	}
 473private:
 474};
 475
 476/**
 477 * Updates the recent people list (those the agent has recently interacted with).
 478 */
 479class LLRecentListUpdater : public LLAvatarListUpdater, public boost::signals2::trackable
 480{
 481	LOG_CLASS(LLRecentListUpdater);
 482
 483public:
 484	LLRecentListUpdater(callback_t cb)
 485	:	LLAvatarListUpdater(cb, 0)
 486	{
 487		LLRecentPeople::instance().setChangedCallback(boost::bind(&LLRecentListUpdater::update, this));
 488	}
 489};
 490
 491//=============================================================================
 492
 493LLPanelPeople::LLPanelPeople()
 494	:	LLPanel(),
 495		mFilterSubString(LLStringUtil::null),
 496		mFilterSubStringOrig(LLStringUtil::null),
 497		mFilterEditor(NULL),
 498		mTabContainer(NULL),
 499		mOnlineFriendList(NULL),
 500		mAllFriendList(NULL),
 501		mNearbyList(NULL),
 502		mRecentList(NULL),
 503		mGroupList(NULL),
 504		mNearbyGearButton(NULL),
 505		mFriendsGearButton(NULL),
 506		mGroupsGearButton(NULL),
 507		mRecentGearButton(NULL),
 508		mMiniMap(NULL)
 509{
 510	mFriendListUpdater = new LLFriendListUpdater(boost::bind(&LLPanelPeople::updateFriendList,	this));
 511	mNearbyListUpdater = new LLNearbyListUpdater(boost::bind(&LLPanelPeople::updateNearbyList,	this));
 512	mRecentListUpdater = new LLRecentListUpdater(boost::bind(&LLPanelPeople::updateRecentList,	this));
 513	mButtonsUpdater = new LLButtonsUpdater(boost::bind(&LLPanelPeople::updateButtons, this));
 514	mCommitCallbackRegistrar.add("People.addFriend", boost::bind(&LLPanelPeople::onAddFriendButtonClicked, this));
 515}
 516
 517LLPanelPeople::~LLPanelPeople()
 518{
 519	delete mButtonsUpdater;
 520	delete mNearbyListUpdater;
 521	delete mFriendListUpdater;
 522	delete mRecentListUpdater;
 523
 524	if(LLVoiceClient::instanceExists())
 525	{
 526		LLVoiceClient::getInstance()->removeObserver(this);
 527	}
 528
 529	if (mGroupPlusMenuHandle.get()) mGroupPlusMenuHandle.get()->die();
 530	if (mNearbyViewSortMenuHandle.get()) mNearbyViewSortMenuHandle.get()->die();
 531	if (mNearbyViewSortMenuHandle.get()) mNearbyViewSortMenuHandle.get()->die();
 532	if (mGroupsViewSortMenuHandle.get()) mGroupsViewSortMenuHandle.get()->die();
 533	if (mRecentViewSortMenuHandle.get()) mRecentViewSortMenuHandle.get()->die();
 534
 535}
 536
 537void LLPanelPeople::onFriendsAccordionExpandedCollapsed(LLUICtrl* ctrl, const LLSD& param, LLAvatarList* avatar_list)
 538{
 539	if(!avatar_list)
 540	{
 541		llerrs << "Bad parameter" << llendl;
 542		return;
 543	}
 544
 545	bool expanded = param.asBoolean();
 546
 547	setAccordionCollapsedByUser(ctrl, !expanded);
 548	if(!expanded)
 549	{
 550		avatar_list->resetSelection();
 551	}
 552}
 553
 554BOOL LLPanelPeople::postBuild()
 555{
 556	mFilterEditor = getChild<LLFilterEditor>("filter_input");
 557	mFilterEditor->setCommitCallback(boost::bind(&LLPanelPeople::onFilterEdit, this, _2));
 558
 559	mTabContainer = getChild<LLTabContainer>("tabs");
 560	mTabContainer->setCommitCallback(boost::bind(&LLPanelPeople::onTabSelected, this, _2));
 561
 562	LLPanel* friends_tab = getChild<LLPanel>(FRIENDS_TAB_NAME);
 563	// updater is active only if panel is visible to user.
 564	friends_tab->setVisibleCallback(boost::bind(&Updater::setActive, mFriendListUpdater, _2));
 565	mOnlineFriendList = friends_tab->getChild<LLAvatarList>("avatars_online");
 566	mAllFriendList = friends_tab->getChild<LLAvatarList>("avatars_all");
 567	mOnlineFriendList->setNoItemsCommentText(getString("no_friends_online"));
 568	mOnlineFriendList->setShowIcons("FriendsListShowIcons");
 569	mOnlineFriendList->showPermissions("FriendsListShowPermissions");
 570	mAllFriendList->setNoItemsCommentText(getString("no_friends"));
 571	mAllFriendList->setShowIcons("FriendsListShowIcons");
 572	mAllFriendList->showPermissions("FriendsListShowPermissions");
 573
 574	LLPanel* nearby_tab = getChild<LLPanel>(NEARBY_TAB_NAME);
 575	nearby_tab->setVisibleCallback(boost::bind(&Updater::setActive, mNearbyListUpdater, _2));
 576	mNearbyList = nearby_tab->getChild<LLAvatarList>("avatar_list");
 577	mNearbyList->setNoItemsCommentText(getString("no_one_near"));
 578	mNearbyList->setNoItemsMsg(getString("no_one_near"));
 579	mNearbyList->setNoFilteredItemsMsg(getString("no_one_filtered_near"));
 580	mNearbyList->setShowIcons("NearbyListShowIcons");
 581	mMiniMap = (LLNetMap*)getChildView("Net Map",true);
 582	mMiniMap->setToolTipMsg(gSavedSettings.getBOOL("DoubleClickTeleport") ? 
 583		getString("AltMiniMapToolTipMsg") :	getString("MiniMapToolTipMsg"));
 584
 585	mRecentList = getChild<LLPanel>(RECENT_TAB_NAME)->getChild<LLAvatarList>("avatar_list");
 586	mRecentList->setNoItemsCommentText(getString("no_recent_people"));
 587	mRecentList->setNoItemsMsg(getString("no_recent_people"));
 588	mRecentList->setNoFilteredItemsMsg(getString("no_filtered_recent_people"));
 589	mRecentList->setShowIcons("RecentListShowIcons");
 590
 591	mGroupList = getChild<LLGroupList>("group_list");
 592	mGroupList->setNoItemsMsg(getString("no_groups_msg"));
 593	mGroupList->setNoFilteredItemsMsg(getString("no_filtered_groups_msg"));
 594
 595	mNearbyList->setContextMenu(&LLPanelPeopleMenus::gNearbyMenu);
 596	mRecentList->setContextMenu(&LLPanelPeopleMenus::gNearbyMenu);
 597	mAllFriendList->setContextMenu(&LLPanelPeopleMenus::gNearbyMenu);
 598	mOnlineFriendList->setContextMenu(&LLPanelPeopleMenus::gNearbyMenu);
 599
 600	setSortOrder(mRecentList,		(ESortOrder)gSavedSettings.getU32("RecentPeopleSortOrder"),	false);
 601	setSortOrder(mAllFriendList,	(ESortOrder)gSavedSettings.getU32("FriendsSortOrder"),		false);
 602	setSortOrder(mNearbyList,		(ESortOrder)gSavedSettings.getU32("NearbyPeopleSortOrder"),	false);
 603
 604	LLPanel* groups_panel = getChild<LLPanel>(GROUP_TAB_NAME);
 605	groups_panel->childSetAction("activate_btn", boost::bind(&LLPanelPeople::onActivateButtonClicked,	this));
 606	groups_panel->childSetAction("plus_btn",	boost::bind(&LLPanelPeople::onGroupPlusButtonClicked,	this));
 607
 608	LLPanel* friends_panel = getChild<LLPanel>(FRIENDS_TAB_NAME);
 609	friends_panel->childSetAction("add_btn",	boost::bind(&LLPanelPeople::onAddFriendWizButtonClicked,	this));
 610	friends_panel->childSetAction("del_btn",	boost::bind(&LLPanelPeople::onDeleteFriendButtonClicked,	this));
 611
 612	mOnlineFriendList->setItemDoubleClickCallback(boost::bind(&LLPanelPeople::onAvatarListDoubleClicked, this, _1));
 613	mAllFriendList->setItemDoubleClickCallback(boost::bind(&LLPanelPeople::onAvatarListDoubleClicked, this, _1));
 614	mNearbyList->setItemDoubleClickCallback(boost::bind(&LLPanelPeople::onAvatarListDoubleClicked, this, _1));
 615	mRecentList->setItemDoubleClickCallback(boost::bind(&LLPanelPeople::onAvatarListDoubleClicked, this, _1));
 616
 617	mOnlineFriendList->setCommitCallback(boost::bind(&LLPanelPeople::onAvatarListCommitted, this, mOnlineFriendList));
 618	mAllFriendList->setCommitCallback(boost::bind(&LLPanelPeople::onAvatarListCommitted, this, mAllFriendList));
 619	mNearbyList->setCommitCallback(boost::bind(&LLPanelPeople::onAvatarListCommitted, this, mNearbyList));
 620	mRecentList->setCommitCallback(boost::bind(&LLPanelPeople::onAvatarListCommitted, this, mRecentList));
 621
 622	// Set openning IM as default on return action for avatar lists
 623	mOnlineFriendList->setReturnCallback(boost::bind(&LLPanelPeople::onImButtonClicked, this));
 624	mAllFriendList->setReturnCallback(boost::bind(&LLPanelPeople::onImButtonClicked, this));
 625	mNearbyList->setReturnCallback(boost::bind(&LLPanelPeople::onImButtonClicked, this));
 626	mRecentList->setReturnCallback(boost::bind(&LLPanelPeople::onImButtonClicked, this));
 627
 628	mGroupList->setDoubleClickCallback(boost::bind(&LLPanelPeople::onChatButtonClicked, this));
 629	mGroupList->setCommitCallback(boost::bind(&LLPanelPeople::updateButtons, this));
 630	mGroupList->setReturnCallback(boost::bind(&LLPanelPeople::onChatButtonClicked, this));
 631
 632	LLAccordionCtrlTab* accordion_tab = getChild<LLAccordionCtrlTab>("tab_all");
 633	accordion_tab->setDropDownStateChangedCallback(
 634		boost::bind(&LLPanelPeople::onFriendsAccordionExpandedCollapsed, this, _1, _2, mAllFriendList));
 635
 636	accordion_tab = getChild<LLAccordionCtrlTab>("tab_online");
 637	accordion_tab->setDropDownStateChangedCallback(
 638		boost::bind(&LLPanelPeople::onFriendsAccordionExpandedCollapsed, this, _1, _2, mOnlineFriendList));
 639
 640	buttonSetAction("view_profile_btn",	boost::bind(&LLPanelPeople::onViewProfileButtonClicked,	this));
 641	buttonSetAction("group_info_btn",	boost::bind(&LLPanelPeople::onGroupInfoButtonClicked,	this));
 642	buttonSetAction("chat_btn",			boost::bind(&LLPanelPeople::onChatButtonClicked,		this));
 643	buttonSetAction("im_btn",			boost::bind(&LLPanelPeople::onImButtonClicked,			this));
 644	buttonSetAction("call_btn",			boost::bind(&LLPanelPeople::onCallButtonClicked,		this));
 645	buttonSetAction("group_call_btn",	boost::bind(&LLPanelPeople::onGroupCallButtonClicked,	this));
 646	buttonSetAction("teleport_btn",		boost::bind(&LLPanelPeople::onTeleportButtonClicked,	this));
 647	buttonSetAction("share_btn",		boost::bind(&LLPanelPeople::onShareButtonClicked,		this));
 648
 649	// Must go after setting commit callback and initializing all pointers to children.
 650	mTabContainer->selectTabByName(NEARBY_TAB_NAME);
 651
 652	// Create menus.
 653	LLUICtrl::CommitCallbackRegistry::ScopedRegistrar registrar;
 654	LLUICtrl::EnableCallbackRegistry::ScopedRegistrar enable_registrar;
 655	
 656	registrar.add("People.Group.Plus.Action",  boost::bind(&LLPanelPeople::onGroupPlusMenuItemClicked,  this, _2));
 657	registrar.add("People.Group.Minus.Action", boost::bind(&LLPanelPeople::onGroupMinusButtonClicked,  this));
 658	registrar.add("People.Friends.ViewSort.Action",  boost::bind(&LLPanelPeople::onFriendsViewSortMenuItemClicked,  this, _2));
 659	registrar.add("People.Nearby.ViewSort.Action",  boost::bind(&LLPanelPeople::onNearbyViewSortMenuItemClicked,  this, _2));
 660	registrar.add("People.Groups.ViewSort.Action",  boost::bind(&LLPanelPeople::onGroupsViewSortMenuItemClicked,  this, _2));
 661	registrar.add("People.Recent.ViewSort.Action",  boost::bind(&LLPanelPeople::onRecentViewSortMenuItemClicked,  this, _2));
 662
 663	enable_registrar.add("People.Group.Minus.Enable",	boost::bind(&LLPanelPeople::isRealGroup,	this));
 664	enable_registrar.add("People.Friends.ViewSort.CheckItem",	boost::bind(&LLPanelPeople::onFriendsViewSortMenuItemCheck,	this, _2));
 665	enable_registrar.add("People.Recent.ViewSort.CheckItem",	boost::bind(&LLPanelPeople::onRecentViewSortMenuItemCheck,	this, _2));
 666	enable_registrar.add("People.Nearby.ViewSort.CheckItem",	boost::bind(&LLPanelPeople::onNearbyViewSortMenuItemCheck,	this, _2));
 667
 668	mNearbyGearButton = getChild<LLMenuButton>("nearby_view_sort_btn");
 669	mFriendsGearButton = getChild<LLMenuButton>("friends_viewsort_btn");
 670	mGroupsGearButton = getChild<LLMenuButton>("groups_viewsort_btn");
 671	mRecentGearButton = getChild<LLMenuButton>("recent_viewsort_btn");
 672
 673	LLMenuGL* plus_menu  = LLUICtrlFactory::getInstance()->createFromFile<LLMenuGL>("menu_group_plus.xml",  gMenuHolder, LLViewerMenuHolderGL::child_registry_t::instance());
 674	mGroupPlusMenuHandle  = plus_menu->getHandle();
 675
 676	LLToggleableMenu* nearby_view_sort  = LLUICtrlFactory::getInstance()->createFromFile<LLToggleableMenu>("menu_people_nearby_view_sort.xml",  gMenuHolder, LLViewerMenuHolderGL::child_registry_t::instance());
 677	if(nearby_view_sort)
 678	{
 679		mNearbyViewSortMenuHandle  = nearby_view_sort->getHandle();
 680		mNearbyGearButton->setMenu(nearby_view_sort);
 681	}
 682
 683	LLToggleableMenu* friend_view_sort  = LLUICtrlFactory::getInstance()->createFromFile<LLToggleableMenu>("menu_people_friends_view_sort.xml",  gMenuHolder, LLViewerMenuHolderGL::child_registry_t::instance());
 684	if(friend_view_sort)
 685	{
 686		mFriendsViewSortMenuHandle  = friend_view_sort->getHandle();
 687		mFriendsGearButton->setMenu(friend_view_sort);
 688	}
 689
 690	LLToggleableMenu* group_view_sort  = LLUICtrlFactory::getInstance()->createFromFile<LLToggleableMenu>("menu_people_groups_view_sort.xml",  gMenuHolder, LLViewerMenuHolderGL::child_registry_t::instance());
 691	if(group_view_sort)
 692	{
 693		mGroupsViewSortMenuHandle  = group_view_sort->getHandle();
 694		mGroupsGearButton->setMenu(group_view_sort);
 695	}
 696
 697	LLToggleableMenu* recent_view_sort  = LLUICtrlFactory::getInstance()->createFromFile<LLToggleableMenu>("menu_people_recent_view_sort.xml",  gMenuHolder, LLViewerMenuHolderGL::child_registry_t::instance());
 698	if(recent_view_sort)
 699	{
 700		mRecentViewSortMenuHandle  = recent_view_sort->getHandle();
 701		mRecentGearButton->setMenu(recent_view_sort);
 702	}
 703
 704	LLVoiceClient::getInstance()->addObserver(this);
 705
 706	// call this method in case some list is empty and buttons can be in inconsistent state
 707	updateButtons();
 708
 709	mOnlineFriendList->setRefreshCompleteCallback(boost::bind(&LLPanelPeople::onFriendListRefreshComplete, this, _1, _2));
 710	mAllFriendList->setRefreshCompleteCallback(boost::bind(&LLPanelPeople::onFriendListRefreshComplete, this, _1, _2));
 711
 712	return TRUE;
 713}
 714
 715// virtual
 716void LLPanelPeople::onChange(EStatusType status, const std::string &channelURI, bool proximal)
 717{
 718	if(status == STATUS_JOINING || status == STATUS_LEFT_CHANNEL)
 719	{
 720		return;
 721	}
 722	
 723	updateButtons();
 724}
 725
 726void LLPanelPeople::updateFriendListHelpText()
 727{
 728	// show special help text for just created account to help finding friends. EXT-4836
 729	static LLTextBox* no_friends_text = getChild<LLTextBox>("no_friends_help_text");
 730
 731	// Seems sometimes all_friends can be empty because of issue with Inventory loading (clear cache, slow connection...)
 732	// So, lets check all lists to avoid overlapping the text with online list. See EXT-6448.
 733	bool any_friend_exists = mAllFriendList->filterHasMatches() || mOnlineFriendList->filterHasMatches();
 734	no_friends_text->setVisible(!any_friend_exists);
 735	if (no_friends_text->getVisible())
 736	{
 737		//update help text for empty lists
 738		std::string message_name = mFilterSubString.empty() ? "no_friends_msg" : "no_filtered_friends_msg";
 739		LLStringUtil::format_map_t args;
 740		args["[SEARCH_TERM]"] = LLURI::escape(mFilterSubStringOrig);
 741		no_friends_text->setText(getString(message_name, args));
 742	}
 743}
 744
 745void LLPanelPeople::updateFriendList()
 746{
 747	if (!mOnlineFriendList || !mAllFriendList)
 748		return;
 749
 750	// get all buddies we know about
 751	const LLAvatarTracker& av_tracker = LLAvatarTracker::instance();
 752	LLAvatarTracker::buddy_map_t all_buddies;
 753	av_tracker.copyBuddyList(all_buddies);
 754
 755	// save them to the online and all friends vectors
 756	uuid_vec_t& online_friendsp = mOnlineFriendList->getIDs();
 757	uuid_vec_t& all_friendsp = mAllFriendList->getIDs();
 758
 759	all_friendsp.clear();
 760	online_friendsp.clear();
 761
 762	uuid_vec_t buddies_uuids;
 763	LLAvatarTracker::buddy_map_t::const_iterator buddies_iter;
 764
 765	// Fill the avatar list with friends UUIDs
 766	for (buddies_iter = all_buddies.begin(); buddies_iter != all_buddies.end(); ++buddies_iter)
 767	{
 768		buddies_uuids.push_back(buddies_iter->first);
 769	}
 770
 771	if (buddies_uuids.size() > 0)
 772	{
 773		lldebugs << "Friends added to the list: " << buddies_uuids.size() << llendl;
 774		all_friendsp = buddies_uuids;
 775	}
 776	else
 777	{
 778		lldebugs << "No friends found" << llendl;
 779	}
 780
 781	LLAvatarTracker::buddy_map_t::const_iterator buddy_it = all_buddies.begin();
 782	for (; buddy_it != all_buddies.end(); ++buddy_it)
 783	{
 784		LLUUID buddy_id = buddy_it->first;
 785		if (av_tracker.isBuddyOnline(buddy_id))
 786			online_friendsp.push_back(buddy_id);
 787	}
 788
 789	/*
 790	 * Avatarlists  will be hidden by showFriendsAccordionsIfNeeded(), if they do not have items.
 791	 * But avatarlist can be updated only if it is visible @see LLAvatarList::draw();   
 792	 * So we need to do force update of lists to avoid inconsistency of data and view of avatarlist. 
 793	 */
 794	mOnlineFriendList->setDirty(true, !mOnlineFriendList->filterHasMatches());// do force update if list do NOT have items
 795	mAllFriendList->setDirty(true, !mAllFriendList->filterHasMatches());
 796	//update trash and other buttons according to a selected item
 797	updateButtons();
 798	showFriendsAccordionsIfNeeded();
 799}
 800
 801void LLPanelPeople::updateNearbyList()
 802{
 803	if (!mNearbyList)
 804		return;
 805
 806	std::vector<LLVector3d> positions;
 807
 808	LLWorld::getInstance()->getAvatars(&mNearbyList->getIDs(), &positions, gAgent.getPositionGlobal(), gSavedSettings.getF32("NearMeRange"));
 809	mNearbyList->setDirty();
 810
 811	DISTANCE_COMPARATOR.updateAvatarsPositions(positions, mNearbyList->getIDs());
 812	LLActiveSpeakerMgr::instance().update(TRUE);
 813}
 814
 815void LLPanelPeople::updateRecentList()
 816{
 817	if (!mRecentList)
 818		return;
 819
 820	LLRecentPeople::instance().get(mRecentList->getIDs());
 821	mRecentList->setDirty();
 822}
 823
 824void LLPanelPeople::buttonSetVisible(std::string btn_name, BOOL visible)
 825{
 826	// To make sure we're referencing the right widget (a child of the button bar).
 827	LLButton* button = getChild<LLView>("button_bar")->getChild<LLButton>(btn_name);
 828	button->setVisible(visible);
 829}
 830
 831void LLPanelPeople::buttonSetEnabled(const std::string& btn_name, bool enabled)
 832{
 833	// To make sure we're referencing the right widget (a child of the button bar).
 834	LLButton* button = getChild<LLView>("button_bar")->getChild<LLButton>(btn_name);
 835	button->setEnabled(enabled);
 836}
 837
 838void LLPanelPeople::buttonSetAction(const std::string& btn_name, const commit_signal_t::slot_type& cb)
 839{
 840	// To make sure we're referencing the right widget (a child of the button bar).
 841	LLButton* button = getChild<LLView>("button_bar")->getChild<LLButton>(btn_name);
 842	button->setClickedCallback(cb);
 843}
 844
 845void LLPanelPeople::updateButtons()
 846{
 847	std::string cur_tab		= getActiveTabName();
 848	bool nearby_tab_active	= (cur_tab == NEARBY_TAB_NAME);
 849	bool friends_tab_active = (cur_tab == FRIENDS_TAB_NAME);
 850	bool group_tab_active	= (cur_tab == GROUP_TAB_NAME);
 851	//bool recent_tab_active	= (cur_tab == RECENT_TAB_NAME);
 852	LLUUID selected_id;
 853
 854	uuid_vec_t selected_uuids;
 855	getCurrentItemIDs(selected_uuids);
 856	bool item_selected = (selected_uuids.size() == 1);
 857	bool multiple_selected = (selected_uuids.size() >= 1);
 858
 859	buttonSetVisible("group_info_btn",		group_tab_active);
 860	buttonSetVisible("chat_btn",			group_tab_active);
 861	buttonSetVisible("view_profile_btn",	!group_tab_active);
 862	buttonSetVisible("im_btn",				!group_tab_active);
 863	buttonSetVisible("call_btn",			!group_tab_active);
 864	buttonSetVisible("group_call_btn",		group_tab_active);
 865	buttonSetVisible("teleport_btn",		friends_tab_active);
 866	buttonSetVisible("share_btn",			nearby_tab_active || friends_tab_active);
 867
 868	if (group_tab_active)
 869	{
 870		bool cur_group_active = true;
 871
 872		if (item_selected)
 873		{
 874			selected_id = mGroupList->getSelectedUUID();
 875			cur_group_active = (gAgent.getGroupID() == selected_id);
 876		}
 877
 878		LLPanel* groups_panel = mTabContainer->getCurrentPanel();
 879		groups_panel->getChildView("activate_btn")->setEnabled(item_selected && !cur_group_active); // "none" or a non-active group selected
 880		groups_panel->getChildView("minus_btn")->setEnabled(item_selected && selected_id.notNull());
 881	}
 882	else
 883	{
 884		bool is_friend = true;
 885
 886		// Check whether selected avatar is our friend.
 887		if (item_selected)
 888		{
 889			selected_id = selected_uuids.front();
 890			is_friend = LLAvatarTracker::instance().getBuddyInfo(selected_id) != NULL;
 891		}
 892
 893		LLPanel* cur_panel = mTabContainer->getCurrentPanel();
 894		if (cur_panel)
 895		{
 896			cur_panel->getChildView("add_friend_btn")->setEnabled(!is_friend);
 897			if (friends_tab_active)
 898			{
 899				cur_panel->getChildView("del_btn")->setEnabled(multiple_selected);
 900			}
 901		}
 902	}
 903
 904	bool enable_calls = LLVoiceClient::getInstance()->isVoiceWorking() && LLVoiceClient::getInstance()->voiceEnabled();
 905
 906	buttonSetEnabled("view_profile_btn",item_selected);
 907	buttonSetEnabled("share_btn",		item_selected);
 908	buttonSetEnabled("im_btn",			multiple_selected); // allow starting the friends conference for multiple selection
 909	buttonSetEnabled("call_btn",		multiple_selected && enable_calls);
 910	buttonSetEnabled("teleport_btn",	multiple_selected && LLAvatarActions::canOfferTeleport(selected_uuids));
 911
 912	bool none_group_selected = item_selected && selected_id.isNull();
 913	buttonSetEnabled("group_info_btn", !none_group_selected);
 914	buttonSetEnabled("group_call_btn", !none_group_selected && enable_calls);
 915	buttonSetEnabled("chat_btn", !none_group_selected);
 916}
 917
 918std::string LLPanelPeople::getActiveTabName() const
 919{
 920	return mTabContainer->getCurrentPanel()->getName();
 921}
 922
 923LLUUID LLPanelPeople::getCurrentItemID() const
 924{
 925	std::string cur_tab = getActiveTabName();
 926
 927	if (cur_tab == FRIENDS_TAB_NAME) // this tab has two lists
 928	{
 929		LLUUID cur_online_friend;
 930
 931		if ((cur_online_friend = mOnlineFriendList->getSelectedUUID()).notNull())
 932			return cur_online_friend;
 933
 934		return mAllFriendList->getSelectedUUID();
 935	}
 936
 937	if (cur_tab == NEARBY_TAB_NAME)
 938		return mNearbyList->getSelectedUUID();
 939
 940	if (cur_tab == RECENT_TAB_NAME)
 941		return mRecentList->getSelectedUUID();
 942
 943	if (cur_tab == GROUP_TAB_NAME)
 944		return mGroupList->getSelectedUUID();
 945
 946	llassert(0 && "unknown tab selected");
 947	return LLUUID::null;
 948}
 949
 950void LLPanelPeople::getCurrentItemIDs(uuid_vec_t& selected_uuids) const
 951{
 952	std::string cur_tab = getActiveTabName();
 953
 954	if (cur_tab == FRIENDS_TAB_NAME)
 955	{
 956		// friends tab has two lists
 957		mOnlineFriendList->getSelectedUUIDs(selected_uuids);
 958		mAllFriendList->getSelectedUUIDs(selected_uuids);
 959	}
 960	else if (cur_tab == NEARBY_TAB_NAME)
 961		mNearbyList->getSelectedUUIDs(selected_uuids);
 962	else if (cur_tab == RECENT_TAB_NAME)
 963		mRecentList->getSelectedUUIDs(selected_uuids);
 964	else if (cur_tab == GROUP_TAB_NAME)
 965		mGroupList->getSelectedUUIDs(selected_uuids);
 966	else
 967		llassert(0 && "unknown tab selected");
 968
 969}
 970
 971void LLPanelPeople::showGroupMenu(LLMenuGL* menu)
 972{
 973	// Shows the menu at the top of the button bar.
 974
 975	// Calculate its coordinates.
 976	// (assumes that groups panel is the current tab)
 977	LLPanel* bottom_panel = mTabContainer->getCurrentPanel()->getChild<LLPanel>("bottom_panel");
 978	LLPanel* parent_panel = mTabContainer->getCurrentPanel();
 979	menu->arrangeAndClear();
 980	S32 menu_height = menu->getRect().getHeight();
 981	S32 menu_x = -2; // *HACK: compensates HPAD in showPopup()
 982	S32 menu_y = bottom_panel->getRect().mTop + menu_height;
 983
 984	// Actually show the menu.
 985	menu->buildDrawLabels();
 986	menu->updateParent(LLMenuGL::sMenuContainer);
 987	LLMenuGL::showPopup(parent_panel, menu, menu_x, menu_y);
 988}
 989
 990void LLPanelPeople::setSortOrder(LLAvatarList* list, ESortOrder order, bool save)
 991{
 992	switch (order)
 993	{
 994	case E_SORT_BY_NAME:
 995		list->sortByName();
 996		break;
 997	case E_SORT_BY_STATUS:
 998		list->setComparator(&STATUS_COMPARATOR);
 999		list->sort();
1000		break;
1001	case E_SORT_BY_MOST_RECENT:
1002		list->setComparator(&RECENT_COMPARATOR);
1003		list->sort();
1004		break;
1005	case E_SORT_BY_RECENT_SPEAKERS:
1006		list->setComparator(&RECENT_SPEAKER_COMPARATOR);
1007		list->sort();
1008		break;
1009	case E_SORT_BY_DISTANCE:
1010		list->setComparator(&DISTANCE_COMPARATOR);
1011		list->sort();
1012		break;
1013	default:
1014		llwarns << "Unrecognized people sort order for " << list->getName() << llendl;
1015		return;
1016	}
1017
1018	if (save)
1019	{
1020		std::string setting;
1021
1022		if (list == mAllFriendList || list == mOnlineFriendList)
1023			setting = "FriendsSortOrder";
1024		else if (list == mRecentList)
1025			setting = "RecentPeopleSortOrder";
1026		else if (list == mNearbyList)
1027			setting = "NearbyPeopleSortOrder";
1028
1029		if (!setting.empty())
1030			gSavedSettings.setU32(setting, order);
1031	}
1032}
1033
1034bool LLPanelPeople::isRealGroup()
1035{
1036	return getCurrentItemID() != LLUUID::null;
1037}
1038
1039void LLPanelPeople::onFilterEdit(const std::string& search_string)
1040{
1041	mFilterSubStringOrig = search_string;
1042	LLStringUtil::trimHead(mFilterSubStringOrig);
1043	// Searches are case-insensitive
1044	std::string search_upper = mFilterSubStringOrig;
1045	LLStringUtil::toUpper(search_upper);
1046
1047	if (mFilterSubString == search_upper)
1048		return;
1049
1050	mFilterSubString = search_upper;
1051
1052	//store accordion tabs state before any manipulation with accordion tabs
1053	if(!mFilterSubString.empty())
1054	{
1055		notifyChildren(LLSD().with("action","store_state"));
1056	}
1057
1058
1059	// Apply new filter.
1060	mNearbyList->setNameFilter(mFilterSubStringOrig);
1061	mOnlineFriendList->setNameFilter(mFilterSubStringOrig);
1062	mAllFriendList->setNameFilter(mFilterSubStringOrig);
1063	mRecentList->setNameFilter(mFilterSubStringOrig);
1064	mGroupList->setNameFilter(mFilterSubStringOrig);
1065
1066	setAccordionCollapsedByUser("tab_online", false);
1067	setAccordionCollapsedByUser("tab_all", false);
1068
1069	showFriendsAccordionsIfNeeded();
1070
1071	//restore accordion tabs state _after_ all manipulations...
1072	if(mFilterSubString.empty())
1073	{
1074		notifyChildren(LLSD().with("action","restore_state"));
1075	}
1076}
1077
1078void LLPanelPeople::onTabSelected(const LLSD& param)
1079{
1080	std::string tab_name = getChild<LLPanel>(param.asString())->getName();
1081	updateButtons();
1082
1083	showFriendsAccordionsIfNeeded();
1084
1085	if (GROUP_TAB_NAME == tab_name)
1086		mFilterEditor->setLabel(getString("groups_filter_label"));
1087	else
1088		mFilterEditor->setLabel(getString("people_filter_label"));
1089}
1090
1091void LLPanelPeople::onAvatarListDoubleClicked(LLUICtrl* ctrl)
1092{
1093	LLAvatarListItem* item = dynamic_cast<LLAvatarListItem*>(ctrl);
1094	if(!item)
1095	{
1096		return;
1097	}
1098
1099	LLUUID clicked_id = item->getAvatarId();
1100	
1101#if 0 // SJB: Useful for testing, but not currently functional or to spec
1102	LLAvatarActions::showProfile(clicked_id);
1103#else // spec says open IM window
1104	LLAvatarActions::startIM(clicked_id);
1105#endif
1106}
1107
1108void LLPanelPeople::onAvatarListCommitted(LLAvatarList* list)
1109{
1110	if (getActiveTabName() == NEARBY_TAB_NAME)
1111	{
1112		uuid_vec_t selected_uuids;
1113		getCurrentItemIDs(selected_uuids);
1114		mMiniMap->setSelected(selected_uuids);
1115	} else
1116	// Make sure only one of the friends lists (online/all) has selection.
1117	if (getActiveTabName() == FRIENDS_TAB_NAME)
1118	{
1119		if (list == mOnlineFriendList)
1120			mAllFriendList->resetSelection(true);
1121		else if (list == mAllFriendList)
1122			mOnlineFriendList->resetSelection(true);
1123		else
1124			llassert(0 && "commit on unknown friends list");
1125	}
1126
1127	updateButtons();
1128}
1129
1130void LLPanelPeople::onViewProfileButtonClicked()
1131{
1132	LLUUID id = getCurrentItemID();
1133	LLAvatarActions::showProfile(id);
1134}
1135
1136void LLPanelPeople::onAddFriendButtonClicked()
1137{
1138	LLUUID id = getCurrentItemID();
1139	if (id.notNull())
1140	{
1141		LLAvatarActions::requestFriendshipDialog(id);
1142	}
1143}
1144
1145bool LLPanelPeople::isItemsFreeOfFriends(const uuid_vec_t& uuids)
1146{
1147	const LLAvatarTracker& av_tracker = LLAvatarTracker::instance();
1148	for ( uuid_vec_t::const_iterator
1149			  id = uuids.begin(),
1150			  id_end = uuids.end();
1151		  id != id_end; ++id )
1152	{
1153		if (av_tracker.isBuddy (*id))
1154		{
1155			return false;
1156		}
1157	}
1158	return true;
1159}
1160
1161void LLPanelPeople::onAddFriendWizButtonClicked()
1162{
1163	// Show add friend wizard.
1164	LLFloaterAvatarPicker* picker = LLFloaterAvatarPicker::show(boost::bind(&LLPanelPeople::onAvatarPicked, _1, _2), FALSE, TRUE);
1165	// Need to disable 'ok' button when friend occurs in selection
1166	if (picker)	picker->setOkBtnEnableCb(boost::bind(&LLPanelPeople::isItemsFreeOfFriends, this, _1));
1167	LLFloater* root_floater = gFloaterView->getParentFloater(this);
1168	if (root_floater)
1169	{
1170		root_floater->addDependentFloater(picker);
1171	}
1172}
1173
1174void LLPanelPeople::onDeleteFriendButtonClicked()
1175{
1176	uuid_vec_t selected_uuids;
1177	getCurrentItemIDs(selected_uuids);
1178
1179	if (selected_uuids.size() == 1)
1180	{
1181		LLAvatarActions::removeFriendDialog( selected_uuids.front() );
1182	}
1183	else if (selected_uuids.size() > 1)
1184	{
1185		LLAvatarActions::removeFriendsDialog( selected_uuids );
1186	}
1187}
1188
1189void LLPanelPeople::onGroupInfoButtonClicked()
1190{
1191	LLGroupActions::show(getCurrentItemID());
1192}
1193
1194void LLPanelPeople::onChatButtonClicked()
1195{
1196	LLUUID group_id = getCurrentItemID();
1197	if (group_id.notNull())
1198		LLGroupActions::startIM(group_id);
1199}
1200
1201void LLPanelPeople::onImButtonClicked()
1202{
1203	uuid_vec_t selected_uuids;
1204	getCurrentItemIDs(selected_uuids);
1205	if ( selected_uuids.size() == 1 )
1206	{
1207		// if selected only one person then start up IM
1208		LLAvatarActions::startIM(selected_uuids.at(0));
1209	}
1210	else if ( selected_uuids.size() > 1 )
1211	{
1212		// for multiple selection start up friends conference
1213		LLAvatarActions::startConference(selected_uuids);
1214	}
1215}
1216
1217void LLPanelPeople::onActivateButtonClicked()
1218{
1219	LLGroupActions::activate(mGroupList->getSelectedUUID());
1220}
1221
1222// static
1223void LLPanelPeople::onAvatarPicked(const uuid_vec_t& ids, const std::vector<LLAvatarName> names)
1224{
1225	if (!names.empty() && !ids.empty())
1226		LLAvatarActions::requestFriendshipDialog(ids[0], names[0].getCompleteName());
1227}
1228
1229void LLPanelPeople::onGroupPlusButtonClicked()
1230{
1231	if (!gAgent.canJoinGroups())
1232	{
1233		LLNotificationsUtil::add("JoinedTooManyGroups");
1234		return;
1235	}
1236
1237	LLMenuGL* plus_menu = (LLMenuGL*)mGroupPlusMenuHandle.get();
1238	if (!plus_menu)
1239		return;
1240
1241	showGroupMenu(plus_menu);
1242}
1243
1244void LLPanelPeople::onGroupMinusButtonClicked()
1245{
1246	LLUUID group_id = getCurrentItemID();
1247	if (group_id.notNull())
1248		LLGroupActions::leave(group_id);
1249}
1250
1251void LLPanelPeople::onGroupPlusMenuItemClicked(const LLSD& userdata)
1252{
1253	std::string chosen_item = userdata.asString();
1254
1255	if (chosen_item == "join_group")
1256		LLGroupActions::search();
1257	else if (chosen_item == "new_group")
1258		LLGroupActions::createGroup();
1259}
1260
1261void LLPanelPeople::onFriendsViewSortMenuItemClicked(const LLSD& userdata)
1262{
1263	std::string chosen_item = userdata.asString();
1264
1265	if (chosen_item == "sort_name")
1266	{
1267		setSortOrder(mAllFriendList, E_SORT_BY_NAME);
1268	}
1269	else if (chosen_item == "sort_status")
1270	{
1271		setSortOrder(mAllFriendList, E_SORT_BY_STATUS);
1272	}
1273	else if (chosen_item == "view_icons")
1274	{
1275		mAllFriendList->toggleIcons();
1276		mOnlineFriendList->toggleIcons();
1277	}
1278	else if (chosen_item == "view_permissions")
1279	{
1280		bool show_permissions = !gSavedSettings.getBOOL("FriendsListShowPermissions");
1281		gSavedSettings.setBOOL("FriendsListShowPermissions", show_permissions);
1282
1283		mAllFriendList->showPermissions(show_permissions);
1284		mOnlineFriendList->showPermissions(show_permissions);
1285	}
1286	else if (chosen_item == "panel_block_list_sidetray")
1287	{
1288		LLFloaterSidePanelContainer::showPanel("people", "panel_block_list_sidetray", LLSD());
1289	}
1290}
1291
1292void LLPanelPeople::onGroupsViewSortMenuItemClicked(const LLSD& userdata)
1293{
1294	std::string chosen_item = userdata.asString();
1295
1296	if (chosen_item == "show_icons")
1297	{
1298		mGroupList->toggleIcons();
1299	}
1300}
1301
1302void LLPanelPeople::onNearbyViewSortMenuItemClicked(const LLSD& userdata)
1303{
1304	std::string chosen_item = userdata.asString();
1305
1306	if (chosen_item == "sort_by_recent_speakers")
1307	{
1308		setSortOrder(mNearbyList, E_SORT_BY_RECENT_SPEAKERS);
1309	}
1310	else if (chosen_item == "sort_name")
1311	{
1312		setSortOrder(mNearbyList, E_SORT_BY_NAME);
1313	}
1314	else if (chosen_item == "view_icons")
1315	{
1316		mNearbyList->toggleIcons();
1317	}
1318	else if (chosen_item == "sort_distance")
1319	{
1320		setSortOrder(mNearbyList, E_SORT_BY_DISTANCE);
1321	}
1322	else if (chosen_item == "panel_block_list_sidetray")
1323	{
1324		LLFloaterSidePanelContainer::showPanel("people", "panel_block_list_sidetray", LLSD());
1325	}
1326}
1327
1328bool LLPanelPeople::onNearbyViewSortMenuItemCheck(const LLSD& userdata)
1329{
1330	std::string item = userdata.asString();
1331	U32 sort_order = gSavedSettings.getU32("NearbyPeopleSortOrder");
1332
1333	if (item == "sort_by_recent_speakers")
1334		return sort_order == E_SORT_BY_RECENT_SPEAKERS;
1335	if (item == "sort_name")
1336		return sort_order == E_SORT_BY_NAME;
1337	if (item == "sort_distance")
1338		return sort_order == E_SORT_BY_DISTANCE;
1339
1340	return false;
1341}
1342
1343void LLPanelPeople::onRecentViewSortMenuItemClicked(const LLSD& userdata)
1344{
1345	std::string chosen_item = userdata.asString();
1346
1347	if (chosen_item == "sort_recent")
1348	{
1349		setSortOrder(mRecentList, E_SORT_BY_MOST_RECENT);
1350	} 
1351	else if (chosen_item == "sort_name")
1352	{
1353		setSortOrder(mRecentList, E_SORT_BY_NAME);
1354	}
1355	else if (chosen_item == "view_icons")
1356	{
1357		mRecentList->toggleIcons();
1358	}
1359	else if (chosen_item == "panel_block_list_sidetray")
1360	{
1361		LLFloaterSidePanelContainer::showPanel("people", "panel_block_list_sidetray", LLSD());
1362	}
1363}
1364
1365bool LLPanelPeople::onFriendsViewSortMenuItemCheck(const LLSD& userdata) 
1366{
1367	std::string item = userdata.asString();
1368	U32 sort_order = gSavedSettings.getU32("FriendsSortOrder");
1369
1370	if (item == "sort_name") 
1371		return sort_order == E_SORT_BY_NAME;
1372	if (item == "sort_status")
1373		return sort_order == E_SORT_BY_STATUS;
1374
1375	return false;
1376}
1377
1378bool LLPanelPeople::onRecentViewSortMenuItemCheck(const LLSD& userdata) 
1379{
1380	std::string item = userdata.asString();
1381	U32 sort_order = gSavedSettings.getU32("RecentPeopleSortOrder");
1382
1383	if (item == "sort_recent")
1384		return sort_order == E_SORT_BY_MOST_RECENT;
1385	if (item == "sort_name") 
1386		return sort_order == E_SORT_BY_NAME;
1387
1388	return false;
1389}
1390
1391void LLPanelPeople::onCallButtonClicked()
1392{
1393	uuid_vec_t selected_uuids;
1394	getCurrentItemIDs(selected_uuids);
1395
1396	if (selected_uuids.size() == 1)
1397	{
1398		// initiate a P2P voice chat with the selected user
1399		LLAvatarActions::startCall(getCurrentItemID());
1400	}
1401	else if (selected_uuids.size() > 1)
1402	{
1403		// initiate an ad-hoc voice chat with multiple users
1404		LLAvatarActions::startAdhocCall(selected_uuids);
1405	}
1406}
1407
1408void LLPanelPeople::onGroupCallButtonClicked()
1409{
1410	LLGroupActions::startCall(getCurrentItemID());
1411}
1412
1413void LLPanelPeople::onTeleportButtonClicked()
1414{
1415	uuid_vec_t selected_uuids;
1416	getCurrentItemIDs(selected_uuids);
1417	LLAvatarActions::offerTeleport(selected_uuids);
1418}
1419
1420void LLPanelPeople::onShareButtonClicked()
1421{
1422	LLAvatarActions::share(getCurrentItemID());
1423}
1424
1425void LLPanelPeople::onMoreButtonClicked()
1426{
1427	// *TODO: not implemented yet
1428}
1429
1430void	LLPanelPeople::onOpen(const LLSD& key)
1431{
1432	std::string tab_name = key["people_panel_tab_name"];
1433	if (!tab_name.empty())
1434		mTabContainer->selectTabByName(tab_name);
1435}
1436
1437bool LLPanelPeople::notifyChildren(const LLSD& info)
1438{
1439	if (info.has("task-panel-action") && info["task-panel-action"].asString() == "handle-tri-state")
1440	{
1441		LLSideTrayPanelContainer* container = dynamic_cast<LLSideTrayPanelContainer*>(getParent());
1442		if (!container)
1443		{
1444			llwarns << "Cannot find People panel container" << llendl;
1445			return true;
1446		}
1447
1448		if (container->getCurrentPanelIndex() > 0) 
1449		{
1450			// if not on the default panel, switch to it
1451			container->onOpen(LLSD().with(LLSideTrayPanelContainer::PARAM_SUB_PANEL_NAME, getName()));
1452		}
1453		else
1454			LLFloaterReg::hideInstance("people");
1455
1456		return true; // this notification is only supposed to be handled by task panels
1457	}
1458
1459	return LLPanel::notifyChildren(info);
1460}
1461
1462void LLPanelPeople::showAccordion(const std::string name, bool show)
1463{
1464	if(name.empty())
1465	{
1466		llwarns << "No name provided" << llendl;
1467		return;
1468	}
1469
1470	LLAccordionCtrlTab* tab = getChild<LLAccordionCtrlTab>(name);
1471	tab->setVisible(show);
1472	if(show)
1473	{
1474		// don't expand accordion if it was collapsed by user
1475		if(!isAccordionCollapsedByUser(tab))
1476		{
1477			// expand accordion
1478			tab->changeOpenClose(false);
1479		}
1480	}
1481}
1482
1483void LLPanelPeople::showFriendsAccordionsIfNeeded()
1484{
1485	if(FRIENDS_TAB_NAME == getActiveTabName())
1486	{
1487		// Expand and show accordions if needed, else - hide them
1488		showAccordion("tab_online", mOnlineFriendList->filterHasMatches());
1489		showAccordion("tab_all", mAllFriendList->filterHasMatches());
1490
1491		// Rearrange accordions
1492		LLAccordionCtrl* accordion = getChild<LLAccordionCtrl>("friends_accordion");
1493		accordion->arrange();
1494
1495		// *TODO: new no_matched_tabs_text attribute was implemented in accordion (EXT-7368).
1496		// this code should be refactored to use it
1497		// keep help text in a synchronization with accordions visibility.
1498		updateFriendListHelpText();
1499	}
1500}
1501
1502void LLPanelPeople::onFriendListRefreshComplete(LLUICtrl*ctrl, const LLSD& param)
1503{
1504	if(ctrl == mOnlineFriendList)
1505	{
1506		showAccordion("tab_online", param.asInteger());
1507	}
1508	else if(ctrl == mAllFriendList)
1509	{
1510		showAccordion("tab_all", param.asInteger());
1511	}
1512}
1513
1514void LLPanelPeople::setAccordionCollapsedByUser(LLUICtrl* acc_tab, bool collapsed)
1515{
1516	if(!acc_tab)
1517	{
1518		llwarns << "Invalid parameter" << llendl;
1519		return;
1520	}
1521
1522	LLSD param = acc_tab->getValue();
1523	param[COLLAPSED_BY_USER] = collapsed;
1524	acc_tab->setValue(param);
1525}
1526
1527void LLPanelPeople::setAccordionCollapsedByUser(const std::string& name, bool collapsed)
1528{
1529	setAccordionCollapsedByUser(getChild<LLUICtrl>(name), collapsed);
1530}
1531
1532bool LLPanelPeople::isAccordionCollapsedByUser(LLUICtrl* acc_tab)
1533{
1534	if(!acc_tab)
1535	{
1536		llwarns << "Invalid parameter" << llendl;
1537		return false;
1538	}
1539
1540	LLSD param = acc_tab->getValue();
1541	if(!param.has(COLLAPSED_BY_USER))
1542	{
1543		return false;
1544	}
1545	return param[COLLAPSED_BY_USER].asBoolean();
1546}
1547
1548bool LLPanelPeople::isAccordionCollapsedByUser(const std::string& name)
1549{
1550	return isAccordionCollapsedByUser(getChild<LLUICtrl>(name));
1551}
1552
1553// EOF