PageRenderTime 145ms CodeModel.GetById 14ms app.highlight 114ms RepoModel.GetById 1ms app.codeStats 1ms

/indra/newview/llparticipantlist.cpp

https://bitbucket.org/lindenlab/viewer-beta/
C++ | 1042 lines | 747 code | 144 blank | 151 comment | 168 complexity | 1bf64e9c0055814d8ec4d7254617f031 MD5 | raw file
   1/** 
   2 * @file llparticipantlist.cpp
   3 * @brief LLParticipantList intended to update view(LLAvatarList) according to incoming messages
   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// common includes
  30#include "lltrans.h"
  31#include "llavataractions.h"
  32#include "llagent.h"
  33
  34#include "llimview.h"
  35#include "llnotificationsutil.h"
  36#include "llparticipantlist.h"
  37#include "llspeakers.h"
  38#include "llviewercontrol.h"
  39#include "llviewermenu.h"
  40#include "llvoiceclient.h"
  41
  42//LLParticipantList retrieves add, clear and remove events and updates view accordingly 
  43#if LL_MSVC
  44#pragma warning (disable : 4355) // 'this' used in initializer list: yes, intentionally
  45#endif
  46
  47static const LLAvatarItemAgentOnTopComparator AGENT_ON_TOP_NAME_COMPARATOR;
  48
  49// helper function to update AvatarList Item's indicator in the voice participant list
  50static void update_speaker_indicator(const LLAvatarList* const avatar_list, const LLUUID& avatar_uuid, bool is_muted)
  51{
  52	LLAvatarListItem* item = dynamic_cast<LLAvatarListItem*>(avatar_list->getItemByValue(avatar_uuid));
  53	if (item)
  54	{
  55		LLOutputMonitorCtrl* indicator = item->getChild<LLOutputMonitorCtrl>("speaking_indicator");
  56		indicator->setIsMuted(is_muted);
  57	}
  58}
  59
  60
  61// See EXT-4301.
  62/**
  63 * class LLAvalineUpdater - observe the list of voice participants in session and check
  64 *  presence of Avaline Callers among them.
  65 *
  66 * LLAvalineUpdater is a LLVoiceClientParticipantObserver. It provides two kinds of validation:
  67 *	- whether Avaline caller presence among participants;
  68 *	- whether watched Avaline caller still exists in voice channel.
  69 * Both validations have callbacks which will notify subscriber if any of event occur.
  70 *
  71 * @see findAvalineCaller()
  72 * @see checkIfAvalineCallersExist()
  73 */
  74class LLAvalineUpdater : public LLVoiceClientParticipantObserver
  75{
  76public:
  77	typedef boost::function<void(const LLUUID& speaker_id)> process_avaline_callback_t;
  78
  79	LLAvalineUpdater(process_avaline_callback_t found_cb, process_avaline_callback_t removed_cb)
  80		: mAvalineFoundCallback(found_cb)
  81		, mAvalineRemovedCallback(removed_cb)
  82	{
  83		LLVoiceClient::getInstance()->addObserver(this);
  84	}
  85	~LLAvalineUpdater()
  86	{
  87		if (LLVoiceClient::instanceExists())
  88		{
  89			LLVoiceClient::getInstance()->removeObserver(this);
  90		}
  91	}
  92
  93	/**
  94	 * Adds UUID of Avaline caller to watch.
  95	 *
  96	 * @see checkIfAvalineCallersExist().
  97	 */
  98	void watchAvalineCaller(const LLUUID& avaline_caller_id)
  99	{
 100		mAvalineCallers.insert(avaline_caller_id);
 101	}
 102
 103	void onParticipantsChanged()
 104	{
 105		uuid_set_t participant_uuids;
 106		LLVoiceClient::getInstance()->getParticipantList(participant_uuids);
 107
 108
 109		// check whether Avaline caller exists among voice participants
 110		// and notify Participant List
 111		findAvalineCaller(participant_uuids);
 112
 113		// check whether watched Avaline callers still present among voice participant
 114		// and remove if absents.
 115		checkIfAvalineCallersExist(participant_uuids);
 116	}
 117
 118private:
 119	typedef std::set<LLUUID> uuid_set_t;
 120
 121	/**
 122	 * Finds Avaline callers among voice participants and calls mAvalineFoundCallback.
 123	 *
 124	 * When Avatar is in group call with Avaline caller and then ends call Avaline caller stays
 125	 * in Group Chat floater (exists in LLSpeakerMgr). If Avatar starts call with that group again
 126	 * Avaline caller is added to voice channel AFTER Avatar is connected to group call.
 127	 * But Voice Control Panel (VCP) is filled from session LLSpeakerMgr and there is no information
 128	 * if a speaker is Avaline caller.
 129	 *
 130	 * In this case this speaker is created as avatar and will be recreated when it appears in
 131	 * Avatar's Voice session.
 132	 *
 133	 * @see LLParticipantList::onAvalineCallerFound()
 134	 */
 135	void findAvalineCaller(const uuid_set_t& participant_uuids)
 136	{
 137		uuid_set_t::const_iterator it = participant_uuids.begin(), it_end = participant_uuids.end();
 138
 139		for(; it != it_end; ++it)
 140		{
 141			const LLUUID& participant_id = *it;
 142			if (!LLVoiceClient::getInstance()->isParticipantAvatar(participant_id))
 143			{
 144				LL_DEBUGS("Avaline") << "Avaline caller found among voice participants: " << participant_id << LL_ENDL;
 145
 146				if (mAvalineFoundCallback)
 147				{
 148					mAvalineFoundCallback(participant_id);
 149				}
 150			}
 151		}
 152	}
 153
 154	/**
 155	 * Finds Avaline callers which are not anymore among voice participants and calls mAvalineRemovedCallback.
 156	 *
 157	 * The problem is when Avaline caller ends a call it is removed from Voice Client session but
 158	 * still exists in LLSpeakerMgr. Server does not send such information.
 159	 * This method implements a HUCK to notify subscribers that watched Avaline callers by class
 160	 * are not anymore in the call.
 161	 *
 162	 * @see LLParticipantList::onAvalineCallerRemoved()
 163	 */
 164	void checkIfAvalineCallersExist(const uuid_set_t& participant_uuids)
 165	{
 166		uuid_set_t::iterator it = mAvalineCallers.begin();
 167		uuid_set_t::const_iterator participants_it_end = participant_uuids.end();
 168
 169		while (it != mAvalineCallers.end())
 170		{
 171			const LLUUID participant_id = *it;
 172			LL_DEBUGS("Avaline") << "Check avaline caller: " << participant_id << LL_ENDL;
 173			bool not_found = participant_uuids.find(participant_id) == participants_it_end;
 174			if (not_found)
 175			{
 176				LL_DEBUGS("Avaline") << "Watched Avaline caller is not found among voice participants: " << participant_id << LL_ENDL;
 177
 178				// notify Participant List
 179				if (mAvalineRemovedCallback)
 180				{
 181					mAvalineRemovedCallback(participant_id);
 182				}
 183
 184				// remove from the watch list
 185				mAvalineCallers.erase(it++);
 186			}
 187			else
 188			{
 189				++it;
 190			}
 191		}
 192	}
 193
 194	process_avaline_callback_t mAvalineFoundCallback;
 195	process_avaline_callback_t mAvalineRemovedCallback;
 196
 197	uuid_set_t mAvalineCallers;
 198};
 199
 200LLParticipantList::LLParticipantList(LLSpeakerMgr* data_source, 
 201									 LLAvatarList* avatar_list,
 202									 bool use_context_menu/* = true*/,
 203									 bool exclude_agent /*= true*/, 
 204									 bool can_toggle_icons /*= true*/) :
 205	mSpeakerMgr(data_source),
 206	mAvatarList(avatar_list),
 207	mParticipantListMenu(NULL),
 208	mExcludeAgent(exclude_agent),
 209	mValidateSpeakerCallback(NULL)
 210{
 211
 212	mAvalineUpdater = new LLAvalineUpdater(boost::bind(&LLParticipantList::onAvalineCallerFound, this, _1),
 213										   boost::bind(&LLParticipantList::onAvalineCallerRemoved, this, _1));
 214
 215	mSpeakerAddListener = new SpeakerAddListener(*this);
 216	mSpeakerRemoveListener = new SpeakerRemoveListener(*this);
 217	mSpeakerClearListener = new SpeakerClearListener(*this);
 218	mSpeakerModeratorListener = new SpeakerModeratorUpdateListener(*this);
 219	mSpeakerMuteListener = new SpeakerMuteListener(*this);
 220
 221	mSpeakerMgr->addListener(mSpeakerAddListener, "add");
 222	mSpeakerMgr->addListener(mSpeakerRemoveListener, "remove");
 223	mSpeakerMgr->addListener(mSpeakerClearListener, "clear");
 224	mSpeakerMgr->addListener(mSpeakerModeratorListener, "update_moderator");
 225
 226	mAvatarList->setNoItemsCommentText(LLTrans::getString("LoadingData"));
 227	LL_DEBUGS("SpeakingIndicator") << "Set session for speaking indicators: " << mSpeakerMgr->getSessionID() << LL_ENDL;
 228	mAvatarList->setSessionID(mSpeakerMgr->getSessionID());
 229	mAvatarListDoubleClickConnection = mAvatarList->setItemDoubleClickCallback(boost::bind(&LLParticipantList::onAvatarListDoubleClicked, this, _1));
 230	mAvatarListRefreshConnection = mAvatarList->setRefreshCompleteCallback(boost::bind(&LLParticipantList::onAvatarListRefreshed, this, _1, _2));
 231    // Set onAvatarListDoubleClicked as default on_return action.
 232	mAvatarListReturnConnection = mAvatarList->setReturnCallback(boost::bind(&LLParticipantList::onAvatarListDoubleClicked, this, mAvatarList));
 233
 234	if (use_context_menu)
 235	{
 236		mParticipantListMenu = new LLParticipantListMenu(*this);
 237		mAvatarList->setContextMenu(mParticipantListMenu);
 238	}
 239	else
 240	{
 241		mAvatarList->setContextMenu(NULL);
 242	}
 243
 244	if (use_context_menu && can_toggle_icons)
 245	{
 246		mAvatarList->setShowIcons("ParticipantListShowIcons");
 247		mAvatarListToggleIconsConnection = gSavedSettings.getControl("ParticipantListShowIcons")->getSignal()->connect(boost::bind(&LLAvatarList::toggleIcons, mAvatarList));
 248	}
 249
 250	//Lets fill avatarList with existing speakers
 251	LLSpeakerMgr::speaker_list_t speaker_list;
 252	mSpeakerMgr->getSpeakerList(&speaker_list, true);
 253	for(LLSpeakerMgr::speaker_list_t::iterator it = speaker_list.begin(); it != speaker_list.end(); it++)
 254	{
 255		const LLPointer<LLSpeaker>& speakerp = *it;
 256
 257		addAvatarIDExceptAgent(speakerp->mID);
 258		if ( speakerp->mIsModerator )
 259		{
 260			mModeratorList.insert(speakerp->mID);
 261		}
 262		else
 263		{
 264			mModeratorToRemoveList.insert(speakerp->mID);
 265		}
 266	}
 267	// we need to exclude agent id for non group chat
 268	sort();
 269}
 270
 271LLParticipantList::~LLParticipantList()
 272{
 273	mAvatarListDoubleClickConnection.disconnect();
 274	mAvatarListRefreshConnection.disconnect();
 275	mAvatarListReturnConnection.disconnect();
 276	mAvatarListToggleIconsConnection.disconnect();
 277
 278	// It is possible Participant List will be re-created from LLCallFloater::onCurrentChannelChanged()
 279	// See ticket EXT-3427
 280	// hide menu before deleting it to stop enable and check handlers from triggering.
 281	if(mParticipantListMenu && !LLApp::isExiting())
 282	{
 283		mParticipantListMenu->hide();
 284	}
 285
 286	if (mParticipantListMenu)
 287	{
 288		delete mParticipantListMenu;
 289		mParticipantListMenu = NULL;
 290	}
 291
 292	mAvatarList->setContextMenu(NULL);
 293	mAvatarList->setComparator(NULL);
 294
 295	delete mAvalineUpdater;
 296}
 297
 298void LLParticipantList::setSpeakingIndicatorsVisible(BOOL visible)
 299{
 300	mAvatarList->setSpeakingIndicatorsVisible(visible);
 301};
 302
 303void LLParticipantList::onAvatarListDoubleClicked(LLUICtrl* ctrl)
 304{
 305	LLAvatarListItem* item = dynamic_cast<LLAvatarListItem*>(ctrl);
 306	if(!item)
 307	{
 308		return;
 309	}
 310
 311	LLUUID clicked_id = item->getAvatarId();
 312
 313	if (clicked_id.isNull() || clicked_id == gAgent.getID())
 314		return;
 315	
 316	LLAvatarActions::startIM(clicked_id);
 317}
 318
 319void LLParticipantList::onAvatarListRefreshed(LLUICtrl* ctrl, const LLSD& param)
 320{
 321	LLAvatarList* list = dynamic_cast<LLAvatarList*>(ctrl);
 322	if (list)
 323	{
 324		const std::string moderator_indicator(LLTrans::getString("IM_moderator_label")); 
 325		const std::size_t moderator_indicator_len = moderator_indicator.length();
 326
 327		// Firstly remove moderators indicator
 328		std::set<LLUUID>::const_iterator
 329			moderator_list_it = mModeratorToRemoveList.begin(),
 330			moderator_list_end = mModeratorToRemoveList.end();
 331		for (;moderator_list_it != moderator_list_end; ++moderator_list_it)
 332		{
 333			LLAvatarListItem* item = dynamic_cast<LLAvatarListItem*> (list->getItemByValue(*moderator_list_it));
 334			if ( item )
 335			{
 336				std::string name = item->getAvatarName();
 337				std::string tooltip = item->getAvatarToolTip();
 338				size_t found = name.find(moderator_indicator);
 339				if (found != std::string::npos)
 340				{
 341					name.erase(found, moderator_indicator_len);
 342					item->setAvatarName(name);
 343				}
 344				found = tooltip.find(moderator_indicator);
 345				if (found != tooltip.npos)
 346				{
 347					tooltip.erase(found, moderator_indicator_len);
 348					item->setAvatarToolTip(tooltip);
 349				}
 350			}
 351		}
 352
 353		mModeratorToRemoveList.clear();
 354
 355		// Add moderators indicator
 356		moderator_list_it = mModeratorList.begin();
 357		moderator_list_end = mModeratorList.end();
 358		for (;moderator_list_it != moderator_list_end; ++moderator_list_it)
 359		{
 360			LLAvatarListItem* item = dynamic_cast<LLAvatarListItem*> (list->getItemByValue(*moderator_list_it));
 361			if ( item )
 362			{
 363				std::string name = item->getAvatarName();
 364				std::string tooltip = item->getAvatarToolTip();
 365				size_t found = name.find(moderator_indicator);
 366				if (found == std::string::npos)
 367				{
 368					name += " ";
 369					name += moderator_indicator;
 370					item->setAvatarName(name);
 371				}
 372				found = tooltip.find(moderator_indicator);
 373				if (found == std::string::npos)
 374				{
 375					tooltip += " ";
 376					tooltip += moderator_indicator;
 377					item->setAvatarToolTip(tooltip);
 378				}
 379			}
 380		}
 381
 382		// update voice mute state of all items. See EXT-7235
 383		LLSpeakerMgr::speaker_list_t speaker_list;
 384
 385		// Use also participants which are not in voice session now (the second arg is TRUE).
 386		// They can already have mModeratorMutedVoice set from the previous voice session
 387		// and LLSpeakerVoiceModerationEvent will not be sent when speaker manager is updated next time.
 388		mSpeakerMgr->getSpeakerList(&speaker_list, TRUE);
 389		for(LLSpeakerMgr::speaker_list_t::iterator it = speaker_list.begin(); it != speaker_list.end(); it++)
 390		{
 391			const LLPointer<LLSpeaker>& speakerp = *it;
 392
 393			update_speaker_indicator(list, speakerp->mID, speakerp->mModeratorMutedVoice);
 394		}
 395	}
 396}
 397
 398/*
 399  Seems this method is not necessary after onAvalineCallerRemoved was implemented;
 400
 401  It does nothing because list item is always created with correct class type for Avaline caller.
 402  For now Avaline Caller is removed from the LLSpeakerMgr List when it is removed from the Voice Client
 403  session.
 404  This happens in two cases: if Avaline Caller ends call itself or if Resident ends group call.
 405
 406  Probably Avaline caller should be removed from the LLSpeakerMgr list ONLY if it ends call itself.
 407  Asked in EXT-4301.
 408*/
 409void LLParticipantList::onAvalineCallerFound(const LLUUID& participant_id)
 410{
 411	LLPanel* item = mAvatarList->getItemByValue(participant_id);
 412
 413	if (NULL == item)
 414	{
 415		LL_WARNS("Avaline") << "Something wrong. Unable to find item for: " << participant_id << LL_ENDL;
 416		return;
 417	}
 418
 419	if (typeid(*item) == typeid(LLAvalineListItem))
 420	{
 421		LL_DEBUGS("Avaline") << "Avaline caller has already correct class type for: " << participant_id << LL_ENDL;
 422		// item representing an Avaline caller has a correct type already.
 423		return;
 424	}
 425
 426	LL_DEBUGS("Avaline") << "remove item from the list and re-add it: " << participant_id << LL_ENDL;
 427
 428	// remove UUID from LLAvatarList::mIDs to be able add it again.
 429	uuid_vec_t& ids = mAvatarList->getIDs();
 430	uuid_vec_t::iterator pos = std::find(ids.begin(), ids.end(), participant_id);
 431	ids.erase(pos);
 432
 433	// remove item directly
 434	mAvatarList->removeItem(item);
 435
 436	// re-add avaline caller with a correct class instance.
 437	addAvatarIDExceptAgent(participant_id);
 438}
 439
 440void LLParticipantList::onAvalineCallerRemoved(const LLUUID& participant_id)
 441{
 442	LL_DEBUGS("Avaline") << "Removing avaline caller from the list: " << participant_id << LL_ENDL;
 443
 444	mSpeakerMgr->removeAvalineSpeaker(participant_id);
 445}
 446
 447void LLParticipantList::setSortOrder(EParticipantSortOrder order)
 448{
 449	const U32 speaker_sort_order = gSavedSettings.getU32("SpeakerParticipantDefaultOrder");
 450
 451	if ( speaker_sort_order != order )
 452	{
 453		gSavedSettings.setU32("SpeakerParticipantDefaultOrder", (U32)order);
 454		sort();
 455	}
 456}
 457
 458const LLParticipantList::EParticipantSortOrder LLParticipantList::getSortOrder() const
 459{
 460	const U32 speaker_sort_order = gSavedSettings.getU32("SpeakerParticipantDefaultOrder");
 461	return EParticipantSortOrder(speaker_sort_order);
 462}
 463
 464void LLParticipantList::setValidateSpeakerCallback(validate_speaker_callback_t cb)
 465{
 466	mValidateSpeakerCallback = cb;
 467}
 468
 469void LLParticipantList::updateRecentSpeakersOrder()
 470{
 471	if (E_SORT_BY_RECENT_SPEAKERS == getSortOrder() && !isHovered())
 472	{
 473		// Need to update speakers to sort list correctly
 474		mSpeakerMgr->update(true);
 475		// Resort avatar list
 476		sort();
 477	}
 478}
 479
 480bool LLParticipantList::isHovered()
 481{
 482	S32 x, y;
 483	LLUI::getMousePositionScreen(&x, &y);
 484	return mAvatarList->calcScreenRect().pointInRect(x, y);
 485}
 486
 487bool LLParticipantList::onAddItemEvent(LLPointer<LLOldEvents::LLEvent> event, const LLSD& userdata)
 488{
 489	LLUUID uu_id = event->getValue().asUUID();
 490
 491	if (mValidateSpeakerCallback && !mValidateSpeakerCallback(uu_id))
 492	{
 493		return true;
 494	}
 495
 496	addAvatarIDExceptAgent(uu_id);
 497	sort();
 498	return true;
 499}
 500
 501bool LLParticipantList::onRemoveItemEvent(LLPointer<LLOldEvents::LLEvent> event, const LLSD& userdata)
 502{
 503	uuid_vec_t& group_members = mAvatarList->getIDs();
 504	uuid_vec_t::iterator pos = std::find(group_members.begin(), group_members.end(), event->getValue().asUUID());
 505	if(pos != group_members.end())
 506	{
 507		group_members.erase(pos);
 508		mAvatarList->setDirty();
 509	}
 510	return true;
 511}
 512
 513bool LLParticipantList::onClearListEvent(LLPointer<LLOldEvents::LLEvent> event, const LLSD& userdata)
 514{
 515	uuid_vec_t& group_members = mAvatarList->getIDs();
 516	group_members.clear();
 517	mAvatarList->setDirty();
 518	return true;
 519}
 520
 521bool LLParticipantList::onModeratorUpdateEvent(LLPointer<LLOldEvents::LLEvent> event, const LLSD& userdata)
 522{
 523	const LLSD& evt_data = event->getValue();
 524	if ( evt_data.has("id") && evt_data.has("is_moderator") )
 525	{
 526		LLUUID id = evt_data["id"];
 527		bool is_moderator = evt_data["is_moderator"];
 528		if ( id.notNull() )
 529		{
 530			if ( is_moderator )
 531				mModeratorList.insert(id);
 532			else
 533			{
 534				std::set<LLUUID>::iterator it = mModeratorList.find (id);
 535				if ( it != mModeratorList.end () )
 536				{
 537					mModeratorToRemoveList.insert(id);
 538					mModeratorList.erase(id);
 539				}
 540			}
 541
 542			// apply changes immediately
 543			onAvatarListRefreshed(mAvatarList, LLSD());
 544		}
 545	}
 546	return true;
 547}
 548
 549bool LLParticipantList::onSpeakerMuteEvent(LLPointer<LLOldEvents::LLEvent> event, const LLSD& userdata)
 550{
 551	LLPointer<LLSpeaker> speakerp = (LLSpeaker*)event->getSource();
 552	if (speakerp.isNull()) return false;
 553
 554	// update UI on confirmation of moderator mutes
 555	if (event->getValue().asString() == "voice")
 556	{
 557		update_speaker_indicator(mAvatarList, speakerp->mID, speakerp->mModeratorMutedVoice);
 558	}
 559	return true;
 560}
 561
 562void LLParticipantList::sort()
 563{
 564	if ( !mAvatarList )
 565		return;
 566
 567	switch ( getSortOrder() ) 
 568	{
 569		case E_SORT_BY_NAME :
 570			// if mExcludeAgent == true , then no need to keep agent on top of the list
 571			if(mExcludeAgent)
 572			{
 573				mAvatarList->sortByName();
 574			}
 575			else
 576			{
 577				mAvatarList->setComparator(&AGENT_ON_TOP_NAME_COMPARATOR);
 578				mAvatarList->sort();
 579			}
 580			break;
 581		case E_SORT_BY_RECENT_SPEAKERS:
 582			if (mSortByRecentSpeakers.isNull())
 583				mSortByRecentSpeakers = new LLAvatarItemRecentSpeakerComparator(*this);
 584			mAvatarList->setComparator(mSortByRecentSpeakers.get());
 585			mAvatarList->sort();
 586			break;
 587		default :
 588			llwarns << "Unrecognized sort order for " << mAvatarList->getName() << llendl;
 589			return;
 590	}
 591}
 592
 593void LLParticipantList::addAvatarIDExceptAgent(const LLUUID& avatar_id)
 594{
 595	if (mExcludeAgent && gAgent.getID() == avatar_id) return;
 596	if (mAvatarList->contains(avatar_id)) return;
 597
 598	bool is_avatar = LLVoiceClient::getInstance()->isParticipantAvatar(avatar_id);
 599
 600	if (is_avatar)
 601	{
 602		mAvatarList->getIDs().push_back(avatar_id);
 603		mAvatarList->setDirty();
 604	}
 605	else
 606	{
 607		std::string display_name = LLVoiceClient::getInstance()->getDisplayName(avatar_id);
 608		mAvatarList->addAvalineItem(avatar_id, mSpeakerMgr->getSessionID(), display_name.empty() ? LLTrans::getString("AvatarNameWaiting") : display_name);
 609		mAvalineUpdater->watchAvalineCaller(avatar_id);
 610	}
 611	adjustParticipant(avatar_id);
 612}
 613
 614void LLParticipantList::adjustParticipant(const LLUUID& speaker_id)
 615{
 616	LLPointer<LLSpeaker> speakerp = mSpeakerMgr->findSpeaker(speaker_id);
 617	if (speakerp.isNull()) return;
 618
 619	// add listener to process moderation changes
 620	speakerp->addListener(mSpeakerMuteListener);
 621}
 622
 623//
 624// LLParticipantList::SpeakerAddListener
 625//
 626bool LLParticipantList::SpeakerAddListener::handleEvent(LLPointer<LLOldEvents::LLEvent> event, const LLSD& userdata)
 627{
 628	/**
 629	 * We need to filter speaking objects. These objects shouldn't appear in the list
 630	 * @see LLFloaterChat::addChat() in llviewermessage.cpp to get detailed call hierarchy
 631	 */
 632	const LLUUID& speaker_id = event->getValue().asUUID();
 633	LLPointer<LLSpeaker> speaker = mParent.mSpeakerMgr->findSpeaker(speaker_id);
 634	if(speaker.isNull() || speaker->mType == LLSpeaker::SPEAKER_OBJECT)
 635	{
 636		return false;
 637	}
 638	return mParent.onAddItemEvent(event, userdata);
 639}
 640
 641//
 642// LLParticipantList::SpeakerRemoveListener
 643//
 644bool LLParticipantList::SpeakerRemoveListener::handleEvent(LLPointer<LLOldEvents::LLEvent> event, const LLSD& userdata)
 645{
 646	return mParent.onRemoveItemEvent(event, userdata);
 647}
 648
 649//
 650// LLParticipantList::SpeakerClearListener
 651//
 652bool LLParticipantList::SpeakerClearListener::handleEvent(LLPointer<LLOldEvents::LLEvent> event, const LLSD& userdata)
 653{
 654	return mParent.onClearListEvent(event, userdata);
 655}
 656
 657//
 658// LLParticipantList::SpeakerModeratorListener
 659//
 660bool LLParticipantList::SpeakerModeratorUpdateListener::handleEvent(LLPointer<LLOldEvents::LLEvent> event, const LLSD& userdata)
 661{
 662	return mParent.onModeratorUpdateEvent(event, userdata);
 663}
 664
 665bool LLParticipantList::SpeakerMuteListener::handleEvent(LLPointer<LLOldEvents::LLEvent> event, const LLSD& userdata)
 666{
 667	return mParent.onSpeakerMuteEvent(event, userdata);
 668}
 669
 670LLContextMenu* LLParticipantList::LLParticipantListMenu::createMenu()
 671{
 672	// set up the callbacks for all of the avatar menu items
 673	LLUICtrl::CommitCallbackRegistry::ScopedRegistrar registrar;
 674	LLUICtrl::EnableCallbackRegistry::ScopedRegistrar enable_registrar;
 675	
 676	registrar.add("ParticipantList.Sort", boost::bind(&LLParticipantList::LLParticipantListMenu::sortParticipantList, this, _2));
 677	registrar.add("ParticipantList.ToggleAllowTextChat", boost::bind(&LLParticipantList::LLParticipantListMenu::toggleAllowTextChat, this, _2));
 678	registrar.add("ParticipantList.ToggleMuteText", boost::bind(&LLParticipantList::LLParticipantListMenu::toggleMuteText, this, _2));
 679
 680	registrar.add("Avatar.Profile",	boost::bind(&LLAvatarActions::showProfile, mUUIDs.front()));
 681	registrar.add("Avatar.IM", boost::bind(&LLAvatarActions::startIM, mUUIDs.front()));
 682	registrar.add("Avatar.AddFriend", boost::bind(&LLAvatarActions::requestFriendshipDialog, mUUIDs.front()));
 683	registrar.add("Avatar.BlockUnblock", boost::bind(&LLParticipantList::LLParticipantListMenu::toggleMuteVoice, this, _2));
 684	registrar.add("Avatar.Share", boost::bind(&LLAvatarActions::share, mUUIDs.front()));
 685	registrar.add("Avatar.Pay",	boost::bind(&LLAvatarActions::pay, mUUIDs.front()));
 686	registrar.add("Avatar.Call", boost::bind(&LLAvatarActions::startCall, mUUIDs.front()));
 687
 688	registrar.add("ParticipantList.ModerateVoice", boost::bind(&LLParticipantList::LLParticipantListMenu::moderateVoice, this, _2));
 689
 690	enable_registrar.add("ParticipantList.EnableItem", boost::bind(&LLParticipantList::LLParticipantListMenu::enableContextMenuItem,	this, _2));
 691	enable_registrar.add("ParticipantList.EnableItem.Moderate", boost::bind(&LLParticipantList::LLParticipantListMenu::enableModerateContextMenuItem,	this, _2));
 692	enable_registrar.add("ParticipantList.CheckItem",  boost::bind(&LLParticipantList::LLParticipantListMenu::checkContextMenuItem,	this, _2));
 693
 694	// create the context menu from the XUI
 695	LLContextMenu* main_menu = createFromFile("menu_participant_list.xml");
 696
 697	// Don't show sort options for P2P chat
 698	bool is_sort_visible = (mParent.mAvatarList && mParent.mAvatarList->size() > 1);
 699	main_menu->setItemVisible("SortByName", is_sort_visible);
 700	main_menu->setItemVisible("SortByRecentSpeakers", is_sort_visible);
 701	main_menu->setItemVisible("Moderator Options Separator", isGroupModerator());
 702	main_menu->setItemVisible("Moderator Options", isGroupModerator());
 703	main_menu->setItemVisible("View Icons Separator", mParent.mAvatarListToggleIconsConnection.connected());
 704	main_menu->setItemVisible("View Icons", mParent.mAvatarListToggleIconsConnection.connected());
 705	main_menu->arrangeAndClear();
 706
 707	return main_menu;
 708}
 709
 710void LLParticipantList::LLParticipantListMenu::show(LLView* spawning_view, const uuid_vec_t& uuids, S32 x, S32 y)
 711{
 712	if (uuids.size() == 0) return;
 713
 714	LLListContextMenu::show(spawning_view, uuids, x, y);
 715
 716	const LLUUID& speaker_id = mUUIDs.front();
 717	BOOL is_muted = isMuted(speaker_id);
 718
 719	if (is_muted)
 720	{
 721		LLMenuGL::sMenuContainer->getChildView("ModerateVoiceMuteSelected")->setVisible( false);
 722	}
 723	else
 724	{
 725		LLMenuGL::sMenuContainer->getChildView("ModerateVoiceUnMuteSelected")->setVisible( false);
 726	}
 727}
 728
 729void LLParticipantList::LLParticipantListMenu::sortParticipantList(const LLSD& userdata)
 730{
 731	std::string param = userdata.asString();
 732	if ("sort_by_name" == param)
 733	{
 734		mParent.setSortOrder(E_SORT_BY_NAME);
 735	}
 736	else if ("sort_by_recent_speakers" == param)
 737	{
 738		mParent.setSortOrder(E_SORT_BY_RECENT_SPEAKERS);
 739	}
 740}
 741
 742void LLParticipantList::LLParticipantListMenu::toggleAllowTextChat(const LLSD& userdata)
 743{
 744
 745	LLIMSpeakerMgr* mgr = dynamic_cast<LLIMSpeakerMgr*>(mParent.mSpeakerMgr);
 746	if (mgr)
 747	{
 748		const LLUUID speaker_id = mUUIDs.front();
 749		mgr->toggleAllowTextChat(speaker_id);
 750	}
 751}
 752
 753void LLParticipantList::LLParticipantListMenu::toggleMute(const LLSD& userdata, U32 flags)
 754{
 755	const LLUUID speaker_id = mUUIDs.front();
 756	BOOL is_muted = LLMuteList::getInstance()->isMuted(speaker_id, flags);
 757	std::string name;
 758
 759	//fill in name using voice client's copy of name cache
 760	LLPointer<LLSpeaker> speakerp = mParent.mSpeakerMgr->findSpeaker(speaker_id);
 761	if (speakerp.isNull())
 762	{
 763		return;
 764	}
 765	LLAvatarListItem* item = dynamic_cast<LLAvatarListItem*>(mParent.mAvatarList->getItemByValue(speaker_id));
 766	if (NULL == item) return;
 767
 768	name = item->getAvatarName();
 769
 770	LLMute::EType mute_type;
 771	switch (speakerp->mType)
 772	{
 773		case LLSpeaker::SPEAKER_AGENT:
 774			mute_type = LLMute::AGENT;
 775			break;
 776		case LLSpeaker::SPEAKER_OBJECT:
 777			mute_type = LLMute::OBJECT;
 778			break;
 779		case LLSpeaker::SPEAKER_EXTERNAL:
 780		default:
 781			mute_type = LLMute::EXTERNAL;
 782			break;
 783	}
 784	LLMute mute(speaker_id, name, mute_type);
 785
 786	if (!is_muted)
 787	{
 788		LLMuteList::getInstance()->add(mute, flags);
 789	}
 790	else
 791	{
 792		LLMuteList::getInstance()->remove(mute, flags);
 793	}
 794}
 795
 796void LLParticipantList::LLParticipantListMenu::toggleMuteText(const LLSD& userdata)
 797{
 798	toggleMute(userdata, LLMute::flagTextChat);
 799}
 800
 801void LLParticipantList::LLParticipantListMenu::toggleMuteVoice(const LLSD& userdata)
 802{
 803	toggleMute(userdata, LLMute::flagVoiceChat);
 804}
 805
 806bool LLParticipantList::LLParticipantListMenu::isGroupModerator()
 807{
 808	if (!mParent.mSpeakerMgr)
 809	{
 810		llwarns << "Speaker manager is missing" << llendl;
 811		return false;
 812	}
 813
 814	// Is session a group call/chat?
 815	if(gAgent.isInGroup(mParent.mSpeakerMgr->getSessionID()))
 816	{
 817		LLSpeaker* speaker = mParent.mSpeakerMgr->findSpeaker(gAgentID).get();
 818
 819		// Is agent a moderator?
 820		return speaker && speaker->mIsModerator;
 821	}
 822	return false;
 823}
 824
 825bool LLParticipantList::LLParticipantListMenu::isMuted(const LLUUID& avatar_id)
 826{
 827	LLPointer<LLSpeaker> selected_speakerp = mParent.mSpeakerMgr->findSpeaker(avatar_id);
 828	if (!selected_speakerp) return true;
 829
 830	return selected_speakerp->mStatus == LLSpeaker::STATUS_MUTED;
 831}
 832
 833void LLParticipantList::LLParticipantListMenu::moderateVoice(const LLSD& userdata)
 834{
 835	if (!gAgent.getRegion()) return;
 836
 837	bool moderate_selected = userdata.asString() == "selected";
 838
 839	if (moderate_selected)
 840	{
 841		const LLUUID& selected_avatar_id = mUUIDs.front();
 842		bool is_muted = isMuted(selected_avatar_id);
 843		moderateVoiceParticipant(selected_avatar_id, is_muted);
 844	}
 845	else
 846	{
 847		bool unmute_all = userdata.asString() == "unmute_all";
 848		moderateVoiceAllParticipants(unmute_all);
 849	}
 850}
 851
 852void LLParticipantList::LLParticipantListMenu::moderateVoiceParticipant(const LLUUID& avatar_id, bool unmute)
 853{
 854	LLIMSpeakerMgr* mgr = dynamic_cast<LLIMSpeakerMgr*>(mParent.mSpeakerMgr);
 855	if (mgr)
 856	{
 857		mgr->moderateVoiceParticipant(avatar_id, unmute);
 858	}
 859}
 860
 861void LLParticipantList::LLParticipantListMenu::moderateVoiceAllParticipants(bool unmute)
 862{
 863	LLIMSpeakerMgr* mgr = dynamic_cast<LLIMSpeakerMgr*>(mParent.mSpeakerMgr);
 864	if (mgr)
 865	{
 866		if (!unmute)
 867		{
 868			LLSD payload;
 869			payload["session_id"] = mgr->getSessionID();
 870			LLNotificationsUtil::add("ConfirmMuteAll", LLSD(), payload, confirmMuteAllCallback);
 871			return;
 872		}
 873
 874		mgr->moderateVoiceAllParticipants(unmute);
 875	}
 876}
 877
 878// static
 879void LLParticipantList::LLParticipantListMenu::confirmMuteAllCallback(const LLSD& notification, const LLSD& response)
 880{
 881	S32 option = LLNotificationsUtil::getSelectedOption(notification, response);
 882	// if Cancel pressed
 883	if (option == 1)
 884	{
 885		return;
 886	}
 887
 888	const LLSD& payload = notification["payload"];
 889	const LLUUID& session_id = payload["session_id"];
 890
 891	LLIMSpeakerMgr * speaker_manager = dynamic_cast<LLIMSpeakerMgr*> (
 892		LLIMModel::getInstance()->getSpeakerManager(session_id));
 893	if (speaker_manager)
 894	{
 895		speaker_manager->moderateVoiceAllParticipants(false);
 896	}
 897
 898	return;
 899}
 900
 901
 902bool LLParticipantList::LLParticipantListMenu::enableContextMenuItem(const LLSD& userdata)
 903{
 904	std::string item = userdata.asString();
 905	const LLUUID& participant_id = mUUIDs.front();
 906
 907	// For now non of "can_view_profile" action and menu actions listed below except "can_block"
 908	// can be performed for Avaline callers.
 909	bool is_participant_avatar = LLVoiceClient::getInstance()->isParticipantAvatar(participant_id);
 910	if (!is_participant_avatar && "can_block" != item) return false;
 911
 912	if (item == "can_mute_text" || "can_block" == item || "can_share" == item || "can_im" == item 
 913		|| "can_pay" == item)
 914	{
 915		return mUUIDs.front() != gAgentID;
 916	}
 917	else if (item == std::string("can_add"))
 918	{
 919		// We can add friends if:
 920		// - there are selected people
 921		// - and there are no friends among selection yet.
 922
 923		bool result = (mUUIDs.size() > 0);
 924
 925		uuid_vec_t::const_iterator
 926			id = mUUIDs.begin(),
 927			uuids_end = mUUIDs.end();
 928
 929		for (;id != uuids_end; ++id)
 930		{
 931			if ( *id == gAgentID || LLAvatarActions::isFriend(*id) )
 932			{
 933				result = false;
 934				break;
 935			}
 936		}
 937		return result;
 938	}
 939	else if (item == "can_call")
 940	{
 941		bool not_agent = mUUIDs.front() != gAgentID;
 942		bool can_call = not_agent &&  LLVoiceClient::getInstance()->voiceEnabled() && LLVoiceClient::getInstance()->isVoiceWorking();
 943		return can_call;
 944	}
 945
 946	return true;
 947}
 948
 949/*
 950  Processed menu items with such parameters:
 951  can_allow_text_chat
 952  can_moderate_voice
 953*/
 954bool LLParticipantList::LLParticipantListMenu::enableModerateContextMenuItem(const LLSD& userdata)
 955{
 956	// only group moderators can perform actions related to this "enable callback"
 957	if (!isGroupModerator()) return false;
 958
 959	const LLUUID& participant_id = mUUIDs.front();
 960	LLPointer<LLSpeaker> speakerp = mParent.mSpeakerMgr->findSpeaker(participant_id);
 961
 962	// not in voice participants can not be moderated
 963	bool speaker_in_voice = speakerp.notNull() && speakerp->isInVoiceChannel();
 964
 965	const std::string& item = userdata.asString();
 966
 967	if ("can_moderate_voice" == item)
 968	{
 969		return speaker_in_voice;
 970	}
 971
 972	// For now non of menu actions except "can_moderate_voice" can be performed for Avaline callers.
 973	bool is_participant_avatar = LLVoiceClient::getInstance()->isParticipantAvatar(participant_id);
 974	if (!is_participant_avatar) return false;
 975
 976	return true;
 977}
 978
 979bool LLParticipantList::LLParticipantListMenu::checkContextMenuItem(const LLSD& userdata)
 980{
 981	std::string item = userdata.asString();
 982	const LLUUID& id = mUUIDs.front();
 983
 984	if (item == "is_muted")
 985	{
 986		return LLMuteList::getInstance()->isMuted(id, LLMute::flagTextChat);
 987	}
 988	else if (item == "is_allowed_text_chat")
 989	{
 990		LLPointer<LLSpeaker> selected_speakerp = mParent.mSpeakerMgr->findSpeaker(id);
 991
 992		if (selected_speakerp.notNull())
 993		{
 994			return !selected_speakerp->mModeratorMutedText;
 995		}
 996	}
 997	else if(item == "is_blocked")
 998	{
 999		return LLMuteList::getInstance()->isMuted(id, LLMute::flagVoiceChat);
1000	}
1001	else if(item == "is_sorted_by_name")
1002	{
1003		return E_SORT_BY_NAME == mParent.getSortOrder();
1004	}
1005	else if(item == "is_sorted_by_recent_speakers")
1006	{
1007		return E_SORT_BY_RECENT_SPEAKERS == mParent.getSortOrder();
1008	}
1009
1010	return false;
1011}
1012
1013bool LLParticipantList::LLAvatarItemRecentSpeakerComparator::doCompare(const LLAvatarListItem* avatar_item1, const LLAvatarListItem* avatar_item2) const
1014{
1015	if (mParent.mSpeakerMgr)
1016	{
1017		LLPointer<LLSpeaker> lhs = mParent.mSpeakerMgr->findSpeaker(avatar_item1->getAvatarId());
1018		LLPointer<LLSpeaker> rhs = mParent.mSpeakerMgr->findSpeaker(avatar_item2->getAvatarId());
1019		if ( lhs.notNull() && rhs.notNull() )
1020		{
1021			// Compare by last speaking time
1022			if( lhs->mLastSpokeTime != rhs->mLastSpokeTime )
1023				return ( lhs->mLastSpokeTime > rhs->mLastSpokeTime );
1024			else if ( lhs->mSortIndex != rhs->mSortIndex )
1025				return ( lhs->mSortIndex < rhs->mSortIndex );
1026		}
1027		else if ( lhs.notNull() )
1028		{
1029			// True if only avatar_item1 speaker info available
1030			return true;
1031		}
1032		else if ( rhs.notNull() )
1033		{
1034			// False if only avatar_item2 speaker info available
1035			return false;
1036		}
1037	}
1038	// By default compare by name.
1039	return LLAvatarItemNameComparator::doCompare(avatar_item1, avatar_item2);
1040}
1041
1042//EOF