diff options
Diffstat (limited to 'indra/newview/llspeakers.cpp')
| -rw-r--r-- | indra/newview/llspeakers.cpp | 2084 | 
1 files changed, 1042 insertions, 1042 deletions
diff --git a/indra/newview/llspeakers.cpp b/indra/newview/llspeakers.cpp index e521378e70..7d71873b68 100644 --- a/indra/newview/llspeakers.cpp +++ b/indra/newview/llspeakers.cpp @@ -1,1042 +1,1042 @@ -/**  - * @file llspeakers.cpp - * @brief Management interface for muting and controlling volume of residents currently speaking - * - * $LicenseInfo:firstyear=2005&license=viewerlgpl$ - * Second Life Viewer Source Code - * Copyright (C) 2010, Linden Research, 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 - *  - * Linden Research, Inc., 945 Battery Street, San Francisco, CA  94111  USA - * $/LicenseInfo$ - */ - -#include "llviewerprecompiledheaders.h" - -#include "llspeakers.h" - -#include "llagent.h" -#include "llavatarnamecache.h" -#include "llappviewer.h" -#include "llimview.h" -#include "llgroupmgr.h" -#include "llsdutil.h" -#include "lluicolortable.h" -#include "llviewerobjectlist.h" -#include "llviewerregion.h" -#include "llvoavatar.h" -#include "llworld.h" -#include "llcorehttputil.h" - -extern LLControlGroup gSavedSettings; - -const LLColor4 INACTIVE_COLOR(0.3f, 0.3f, 0.3f, 0.5f); -const LLColor4 ACTIVE_COLOR(0.5f, 0.5f, 0.5f, 1.f); - -LLSpeaker::LLSpeaker(const LLUUID& id, const std::string& name, const ESpeakerType type) :  -	mStatus(LLSpeaker::STATUS_TEXT_ONLY), -	mLastSpokeTime(0.f),  -	mSpeechVolume(0.f),  -	mHasSpoken(false), -	mHasLeftCurrentCall(false), -	mDotColor(LLColor4::white), -	mID(id), -	mTyping(false), -	mSortIndex(0), -	mType(type), -	mIsModerator(false), -	mModeratorMutedVoice(false), -	mModeratorMutedText(false) -{ -	if (name.empty() && type == SPEAKER_AGENT) -	{ -		lookupName(); -	} -	else -	{ -		mDisplayName = name; -	} -} - - -void LLSpeaker::lookupName() -{ -	if (mDisplayName.empty()) -	{ -		LLAvatarNameCache::get(mID, boost::bind(&LLSpeaker::onNameCache, this, _1, _2)); // todo: can be group??? -	} -} - -void LLSpeaker::onNameCache(const LLUUID& id, const LLAvatarName& av_name) -{ -	mDisplayName = av_name.getUserName(); -} - -bool LLSpeaker::isInVoiceChannel() -{ -	return mStatus <= LLSpeaker::STATUS_VOICE_ACTIVE || mStatus == LLSpeaker::STATUS_MUTED; -} - -LLSpeakerUpdateSpeakerEvent::LLSpeakerUpdateSpeakerEvent(LLSpeaker* source) -: LLEvent(source, "Speaker update speaker event"), -  mSpeakerID (source->mID) -{ -} - -LLSD LLSpeakerUpdateSpeakerEvent::getValue() -{ -	LLSD ret; -	ret["id"] = mSpeakerID; -	return ret; -} - -LLSpeakerUpdateModeratorEvent::LLSpeakerUpdateModeratorEvent(LLSpeaker* source) -: LLEvent(source, "Speaker add moderator event"), -  mSpeakerID (source->mID), -  mIsModerator (source->mIsModerator) -{ -} - -LLSD LLSpeakerUpdateModeratorEvent::getValue() -{ -	LLSD ret; -	ret["id"] = mSpeakerID; -	ret["is_moderator"] = mIsModerator; -	return ret; -} - -LLSpeakerTextModerationEvent::LLSpeakerTextModerationEvent(LLSpeaker* source) -: LLEvent(source, "Speaker text moderation event") -{ -} - -LLSD LLSpeakerTextModerationEvent::getValue() -{ -	return std::string("text"); -} - - -LLSpeakerVoiceModerationEvent::LLSpeakerVoiceModerationEvent(LLSpeaker* source) -: LLEvent(source, "Speaker voice moderation event") -{ -} - -LLSD LLSpeakerVoiceModerationEvent::getValue() -{ -	return std::string("voice"); -} - -LLSpeakerListChangeEvent::LLSpeakerListChangeEvent(LLSpeakerMgr* source, const LLUUID& speaker_id) -: LLEvent(source, "Speaker added/removed from speaker mgr"), -  mSpeakerID(speaker_id) -{ -} - -LLSD LLSpeakerListChangeEvent::getValue() -{ -	return mSpeakerID; -} - -// helper sort class -struct LLSortRecentSpeakers -{ -	bool operator()(const LLPointer<LLSpeaker> lhs, const LLPointer<LLSpeaker> rhs) const; -}; - -bool LLSortRecentSpeakers::operator()(const LLPointer<LLSpeaker> lhs, const LLPointer<LLSpeaker> rhs) const -{ -	// Sort first on status -	if (lhs->mStatus != rhs->mStatus)  -	{ -		return (lhs->mStatus < rhs->mStatus); -	} - -	// and then on last speaking time -	if(lhs->mLastSpokeTime != rhs->mLastSpokeTime) -	{ -		return (lhs->mLastSpokeTime > rhs->mLastSpokeTime); -	} -	 -	// and finally (only if those are both equal), on name. -	return(	lhs->mDisplayName.compare(rhs->mDisplayName) < 0 ); -} - -LLSpeakerActionTimer::LLSpeakerActionTimer(action_callback_t action_cb, F32 action_period, const LLUUID& speaker_id) -: LLEventTimer(action_period) -, mActionCallback(action_cb) -, mSpeakerId(speaker_id) -{ -} - -bool LLSpeakerActionTimer::tick() -{ -	if (mActionCallback) -	{ -		return (bool)mActionCallback(mSpeakerId); -	} -	return true; -} - -void LLSpeakerActionTimer::unset() -{ -	mActionCallback = 0; -} - -LLSpeakersDelayActionsStorage::LLSpeakersDelayActionsStorage(LLSpeakerActionTimer::action_callback_t action_cb, F32 action_delay) -: mActionCallback(action_cb) -, mActionDelay(action_delay) -{ -} - -LLSpeakersDelayActionsStorage::~LLSpeakersDelayActionsStorage() -{ -	removeAllTimers(); -} - -void LLSpeakersDelayActionsStorage::setActionTimer(const LLUUID& speaker_id) -{ -	bool not_found = true; -	if (mActionTimersMap.size() > 0) -	{ -		not_found = mActionTimersMap.find(speaker_id) == mActionTimersMap.end(); -	} - -	// If there is already a started timer for the passed UUID don't do anything. -	if (not_found) -	{ -		// Starting a timer to remove an participant after delay is completed -		mActionTimersMap.insert(LLSpeakerActionTimer::action_value_t(speaker_id, -			new LLSpeakerActionTimer( -				boost::bind(&LLSpeakersDelayActionsStorage::onTimerActionCallback, this, _1), -				mActionDelay, speaker_id))); -	} -} - -void LLSpeakersDelayActionsStorage::unsetActionTimer(const LLUUID& speaker_id) -{ -	if (mActionTimersMap.size() == 0) return; - -	LLSpeakerActionTimer::action_timer_iter_t it_speaker = mActionTimersMap.find(speaker_id); - -	if (it_speaker != mActionTimersMap.end()) -	{ -		it_speaker->second->unset(); -		mActionTimersMap.erase(it_speaker); -	} -} - -void LLSpeakersDelayActionsStorage::removeAllTimers() -{ -	LLSpeakerActionTimer::action_timer_iter_t iter = mActionTimersMap.begin(); -	for (; iter != mActionTimersMap.end(); ++iter) -	{ -		delete iter->second; -	} -	mActionTimersMap.clear(); -} - -bool LLSpeakersDelayActionsStorage::onTimerActionCallback(const LLUUID& speaker_id) -{ -	unsetActionTimer(speaker_id); - -	if (mActionCallback) -	{ -		mActionCallback(speaker_id); -	} - -	return true; -} - -bool LLSpeakersDelayActionsStorage::isTimerStarted(const LLUUID& speaker_id) -{ -	return (mActionTimersMap.size() > 0) && (mActionTimersMap.find(speaker_id) != mActionTimersMap.end()); -} - -// -// LLSpeakerMgr -// - -LLSpeakerMgr::LLSpeakerMgr(LLVoiceChannel* channelp) :  -	mVoiceChannel(channelp), -	mVoiceModerated(false), -	mModerateModeHandledFirstTime(false), -	mSpeakerListUpdated(false) -{ -    mGetListTime.reset(); -	static LLUICachedControl<F32> remove_delay ("SpeakerParticipantRemoveDelay", 10.0); - -	mSpeakerDelayRemover = new LLSpeakersDelayActionsStorage(boost::bind(&LLSpeakerMgr::removeSpeaker, this, _1), remove_delay); -} - -LLSpeakerMgr::~LLSpeakerMgr() -{ -	delete mSpeakerDelayRemover; -} - -LLPointer<LLSpeaker> LLSpeakerMgr::setSpeaker(const LLUUID& id, const std::string& name, LLSpeaker::ESpeakerStatus status, LLSpeaker::ESpeakerType type) -{ -	LLUUID session_id = getSessionID(); -	if (id.isNull() || (id == session_id)) -	{ -		return NULL; -	} - -	LLPointer<LLSpeaker> speakerp; -	if (mSpeakers.find(id) == mSpeakers.end()) -	{ -		speakerp = new LLSpeaker(id, name, type); -		speakerp->mStatus = status; -		mSpeakers.insert(std::make_pair(speakerp->mID, speakerp)); -		mSpeakersSorted.push_back(speakerp); -		LL_DEBUGS("Speakers") << "Added speaker " << id << LL_ENDL; -		fireEvent(new LLSpeakerListChangeEvent(this, speakerp->mID), "add"); -	} -	else -	{ -		speakerp = findSpeaker(id); -		if (speakerp.notNull()) -		{ -			// keep highest priority status (lowest value) instead of overriding current value -			speakerp->mStatus = llmin(speakerp->mStatus, status); -			// RN: due to a weird behavior where IMs from attached objects come from the wearer's agent_id -			// we need to override speakers that we think are objects when we find out they are really -			// residents -			if (type == LLSpeaker::SPEAKER_AGENT) -			{ -				speakerp->mType = LLSpeaker::SPEAKER_AGENT; -				speakerp->lookupName(); -			} -		} -		else -		{ -			LL_WARNS("Speakers") << "Speaker " << id << " not found" << LL_ENDL; -		} -	} - -	mSpeakerDelayRemover->unsetActionTimer(speakerp->mID); -	return speakerp; -} - -// *TODO: Once way to request the current voice channel moderation mode is implemented -// this method with related code should be removed. -/* - Initializes "moderate_mode" of voice session on first join. -  - This is WORKAROUND because a way to request the current voice channel moderation mode exists - but is not implemented in viewer yet. See EXT-6937. -*/ -void LLSpeakerMgr::initVoiceModerateMode() -{ -	if (!mModerateModeHandledFirstTime && (mVoiceChannel && mVoiceChannel->isActive())) -	{ -		LLPointer<LLSpeaker> speakerp; - -		if (mSpeakers.find(gAgentID) != mSpeakers.end()) -		{ -			speakerp = mSpeakers[gAgentID]; -		} - -		if (speakerp.notNull()) -		{ -			mVoiceModerated = speakerp->mModeratorMutedVoice; -			mModerateModeHandledFirstTime = true; -		} -	} -} - -void LLSpeakerMgr::update(bool resort_ok) -{ -	if (!LLVoiceClient::getInstance()) -	{ -		return; -	} -	 -	LLColor4 speaking_color = LLUIColorTable::instance().getColor("SpeakingColor"); -	LLColor4 overdriven_color = LLUIColorTable::instance().getColor("OverdrivenColor"); - -	if(resort_ok) // only allow list changes when user is not interacting with it -	{ -		updateSpeakerList(); -	} - -	// update status of all current speakers -	bool voice_channel_active = (!mVoiceChannel && LLVoiceClient::getInstance()->inProximalChannel()) || (mVoiceChannel && mVoiceChannel->isActive()); -	for (speaker_map_t::iterator speaker_it = mSpeakers.begin(); speaker_it != mSpeakers.end(); speaker_it++) -	{ -		LLUUID speaker_id = speaker_it->first; -		LLSpeaker* speakerp = speaker_it->second; - -		if (voice_channel_active && LLVoiceClient::getInstance()->getVoiceEnabled(speaker_id)) -		{ -			speakerp->mSpeechVolume = LLVoiceClient::getInstance()->getCurrentPower(speaker_id); -			bool moderator_muted_voice = LLVoiceClient::getInstance()->getIsModeratorMuted(speaker_id); -			if (moderator_muted_voice != speakerp->mModeratorMutedVoice) -			{ -				speakerp->mModeratorMutedVoice = moderator_muted_voice; -				LL_DEBUGS("Speakers") << (speakerp->mModeratorMutedVoice? "Muted" : "Umuted") << " speaker " << speaker_id<< LL_ENDL; -				speakerp->fireEvent(new LLSpeakerVoiceModerationEvent(speakerp)); -			} - -			if (LLVoiceClient::getInstance()->getOnMuteList(speaker_id) || speakerp->mModeratorMutedVoice) -			{ -				speakerp->mStatus = LLSpeaker::STATUS_MUTED; -			} -			else if (LLVoiceClient::getInstance()->getIsSpeaking(speaker_id)) -			{ -				// reset inactivity expiration -				if (speakerp->mStatus != LLSpeaker::STATUS_SPEAKING) -				{ -					speakerp->mLastSpokeTime = mSpeechTimer.getElapsedTimeF32(); -					speakerp->mHasSpoken = true; -					fireEvent(new LLSpeakerUpdateSpeakerEvent(speakerp), "update_speaker"); -				} -				speakerp->mStatus = LLSpeaker::STATUS_SPEAKING; -				// interpolate between active color and full speaking color based on power of speech output -				speakerp->mDotColor = speaking_color; -				if (speakerp->mSpeechVolume > LLVoiceClient::OVERDRIVEN_POWER_LEVEL) -				{ -					speakerp->mDotColor = overdriven_color; -				} -			} -			else -			{ -				speakerp->mSpeechVolume = 0.f; -				speakerp->mDotColor = ACTIVE_COLOR; - -				if (speakerp->mHasSpoken) -				{ -					// have spoken once, not currently speaking -					speakerp->mStatus = LLSpeaker::STATUS_HAS_SPOKEN; -				} -				else -				{ -					// default state for being in voice channel -					speakerp->mStatus = LLSpeaker::STATUS_VOICE_ACTIVE; -				} -			} -		} -		// speaker no longer registered in voice channel, demote to text only -		else if (speakerp->mStatus != LLSpeaker::STATUS_NOT_IN_CHANNEL) -		{ -			if(speakerp->mType == LLSpeaker::SPEAKER_EXTERNAL) -			{ -				// external speakers should be timed out when they leave the voice channel (since they only exist via SLVoice) -				speakerp->mStatus = LLSpeaker::STATUS_NOT_IN_CHANNEL; -			} -			else -			{ -				speakerp->mStatus = LLSpeaker::STATUS_TEXT_ONLY; -				speakerp->mSpeechVolume = 0.f; -				speakerp->mDotColor = ACTIVE_COLOR; -			} -		} -	} - -	if(resort_ok)  // only allow list changes when user is not interacting with it -	{ -		// sort by status then time last spoken -		std::sort(mSpeakersSorted.begin(), mSpeakersSorted.end(), LLSortRecentSpeakers()); -	} - -	// for recent speakers who are not currently speaking, show "recent" color dot for most recent -	// fading to "active" color - -	S32 recent_speaker_count = 0; -	S32 sort_index = 0; -	speaker_list_t::iterator sorted_speaker_it; -	for(sorted_speaker_it = mSpeakersSorted.begin();  -		sorted_speaker_it != mSpeakersSorted.end(); ++sorted_speaker_it) -	{ -		LLPointer<LLSpeaker> speakerp = *sorted_speaker_it; -		 -		// color code recent speakers who are not currently speaking -		if (speakerp->mStatus == LLSpeaker::STATUS_HAS_SPOKEN) -		{ -			speakerp->mDotColor = lerp(speaking_color, ACTIVE_COLOR, clamp_rescale((F32)recent_speaker_count, -2.f, 3.f, 0.f, 1.f)); -			recent_speaker_count++; -		} - -		// stuff sort ordinal into speaker so the ui can sort by this value -		speakerp->mSortIndex = sort_index++; -	} -} - -void LLSpeakerMgr::updateSpeakerList() -{ -	// Are we bound to the currently active voice channel? -	if ((!mVoiceChannel && LLVoiceClient::getInstance()->inProximalChannel()) || (mVoiceChannel && mVoiceChannel->isActive())) -	{ -		std::set<LLUUID> participants; -		LLVoiceClient::getInstance()->getParticipantList(participants); -		// If we are, add all voice client participants to our list of known speakers -		for (std::set<LLUUID>::iterator participant_it = participants.begin(); participant_it != participants.end(); ++participant_it) -		{ -				setSpeaker(*participant_it,  -						   LLVoiceClient::getInstance()->getDisplayName(*participant_it), -						   LLSpeaker::STATUS_VOICE_ACTIVE,  -						   (LLVoiceClient::getInstance()->isParticipantAvatar(*participant_it)?LLSpeaker::SPEAKER_AGENT:LLSpeaker::SPEAKER_EXTERNAL)); -		} -	} -	else  -	{ -		// If not, check if the list is empty, except if it's Nearby Chat (session_id NULL). -		LLUUID session_id = getSessionID(); -		if (!session_id.isNull() && !mSpeakerListUpdated) -		{ -			// If the list is empty, we update it with whatever we have locally so that it doesn't stay empty too long. -			// *TODO: Fix the server side code that sometimes forgets to send back the list of participants after a chat started. -			// (IOW, fix why we get no ChatterBoxSessionAgentListUpdates message after the initial ChatterBoxSessionStartReply) -			LLIMModel::LLIMSession* session = LLIMModel::getInstance()->findIMSession(session_id); -			if (session->isGroupSessionType() && (mSpeakers.size() <= 1)) -			{ -				// For groups, we need to hit the group manager. -				// Note: The session uuid and the group uuid are actually one and the same. If that was to change, this will fail. -				LLGroupMgrGroupData* gdatap = LLGroupMgr::getInstance()->getGroupData(session_id); - -				if (gdatap && gdatap->isMemberDataComplete() && !gdatap->mMembers.empty()) -				{ -					// Add group members when we get the complete list (note: can take a while before we get that list) -					LLGroupMgrGroupData::member_list_t::iterator member_it = gdatap->mMembers.begin(); -                    const S32 load_group_max_members = gSavedSettings.getS32("ChatLoadGroupMaxMembers"); -                    S32 updated = 0; -					while (member_it != gdatap->mMembers.end()) -					{ -						LLGroupMemberData* member = member_it->second; -                        LLUUID id = member_it->first; -						// Add only members who are online and not already in the list -						if ((member->getOnlineStatus() == "Online") && (mSpeakers.find(id) == mSpeakers.end())) -						{ -							LLPointer<LLSpeaker> speakerp = setSpeaker(id, "", LLSpeaker::STATUS_VOICE_ACTIVE, LLSpeaker::SPEAKER_AGENT); -							speakerp->mIsModerator = ((member->getAgentPowers() & GP_SESSION_MODERATOR) == GP_SESSION_MODERATOR); -                            updated++; -						} -						++member_it; -                        // Limit the number of "manually updated" participants to a reasonable number to avoid severe fps drop -                        // *TODO : solve the perf issue of having several hundreds of widgets in the conversation list -                        if (updated >= load_group_max_members) -                            break; -					} -                    mSpeakerListUpdated = true; -				} -			} -			else if (mSpeakers.size() == 0) -			{ -				// For all other session type (ad-hoc, P2P), we use the initial participants targets list -				for (uuid_vec_t::iterator it = session->mInitialTargetIDs.begin();it!=session->mInitialTargetIDs.end();++it) -				{ -					// Add buddies if they are on line, add any other avatar. -					if (!LLAvatarTracker::instance().isBuddy(*it) || LLAvatarTracker::instance().isBuddyOnline(*it)) -					{ -						setSpeaker(*it, "", LLSpeaker::STATUS_VOICE_ACTIVE, LLSpeaker::SPEAKER_AGENT); -					} -				} -				mSpeakerListUpdated = true; -			} -			else -			{ -				// The list has been updated the normal way (i.e. by a ChatterBoxSessionAgentListUpdates received from the server) -				mSpeakerListUpdated = true; -			} -		} -	} -	// Always add the current agent (it has to be there...). Will do nothing if already there. -	setSpeaker(gAgentID, "", LLSpeaker::STATUS_VOICE_ACTIVE, LLSpeaker::SPEAKER_AGENT); -} - -void LLSpeakerMgr::setSpeakerNotInChannel(LLPointer<LLSpeaker> speakerp) -{ -	if  (speakerp.notNull()) -	{ -		speakerp->mStatus = LLSpeaker::STATUS_NOT_IN_CHANNEL; -		speakerp->mDotColor = INACTIVE_COLOR; -		mSpeakerDelayRemover->setActionTimer(speakerp->mID); -	} -} - -bool LLSpeakerMgr::removeSpeaker(const LLUUID& speaker_id) -{ -	mSpeakers.erase(speaker_id); - -	speaker_list_t::iterator sorted_speaker_it = mSpeakersSorted.begin(); -	 -	for(; sorted_speaker_it != mSpeakersSorted.end(); ++sorted_speaker_it) -	{ -		if (speaker_id == (*sorted_speaker_it)->mID) -		{ -			mSpeakersSorted.erase(sorted_speaker_it); -			break; -		} -	} - -	LL_DEBUGS("Speakers") << "Removed speaker " << speaker_id << LL_ENDL; -	fireEvent(new LLSpeakerListChangeEvent(this, speaker_id), "remove"); - -	update(true); - -	return false; -} - -LLPointer<LLSpeaker> LLSpeakerMgr::findSpeaker(const LLUUID& speaker_id) -{ -	//In some conditions map causes crash if it is empty(Windows only), adding check (EK) -	if (mSpeakers.size() == 0) -		return NULL; -	speaker_map_t::iterator found_it = mSpeakers.find(speaker_id); -	if (found_it == mSpeakers.end()) -	{ -		return NULL; -	} -	return found_it->second; -} - -void LLSpeakerMgr::getSpeakerList(speaker_list_t* speaker_list, bool include_text) -{ -	speaker_list->clear(); -	for (speaker_map_t::iterator speaker_it = mSpeakers.begin(); speaker_it != mSpeakers.end(); ++speaker_it) -	{ -		LLPointer<LLSpeaker> speakerp = speaker_it->second; -		// what about text only muted or inactive? -		if (include_text || speakerp->mStatus != LLSpeaker::STATUS_TEXT_ONLY) -		{ -			speaker_list->push_back(speakerp); -		} -	} -} - -const LLUUID LLSpeakerMgr::getSessionID()  -{  -	return mVoiceChannel->getSessionID();  -} - -bool LLSpeakerMgr::isSpeakerToBeRemoved(const LLUUID& speaker_id) -{ -	return mSpeakerDelayRemover && mSpeakerDelayRemover->isTimerStarted(speaker_id); -} - -void LLSpeakerMgr::setSpeakerTyping(const LLUUID& speaker_id, bool typing) -{ -	LLPointer<LLSpeaker> speakerp = findSpeaker(speaker_id); -	if (speakerp.notNull()) -	{ -		speakerp->mTyping = typing; -	} -} - -// speaker has chatted via either text or voice -void LLSpeakerMgr::speakerChatted(const LLUUID& speaker_id) -{ -	LLPointer<LLSpeaker> speakerp = findSpeaker(speaker_id); -	if (speakerp.notNull()) -	{ -		speakerp->mLastSpokeTime = mSpeechTimer.getElapsedTimeF32(); -		speakerp->mHasSpoken = true; -		fireEvent(new LLSpeakerUpdateSpeakerEvent(speakerp), "update_speaker"); -	} -} - -bool LLSpeakerMgr::isVoiceActive() -{ -	// mVoiceChannel = NULL means current voice channel, whatever it is -	return LLVoiceClient::getInstance()->voiceEnabled() && mVoiceChannel && mVoiceChannel->isActive(); -} - - -// -// LLIMSpeakerMgr -// -LLIMSpeakerMgr::LLIMSpeakerMgr(LLVoiceChannel* channel) : LLSpeakerMgr(channel) -{ -} - -void LLIMSpeakerMgr::updateSpeakerList() -{ -	// don't do normal updates which are pulled from voice channel -	// rely on user list reported by sim -	 -	// We need to do this to allow PSTN callers into group chats to show in the list. -	LLSpeakerMgr::updateSpeakerList(); -	 -	return; -} - -void LLIMSpeakerMgr::setSpeakers(const LLSD& speakers) -{ -	if ( !speakers.isMap() ) return; - -	if ( speakers.has("agent_info") && speakers["agent_info"].isMap() ) -	{ -		LLSD::map_const_iterator speaker_it; -		for(speaker_it = speakers["agent_info"].beginMap(); -			speaker_it != speakers["agent_info"].endMap(); -			++speaker_it) -		{ -			LLUUID agent_id(speaker_it->first); - -			LLPointer<LLSpeaker> speakerp = setSpeaker( -				agent_id, -				LLStringUtil::null, -				LLSpeaker::STATUS_TEXT_ONLY); - -			if ( speaker_it->second.isMap() ) -			{ -				bool is_moderator = speakerp->mIsModerator; -				speakerp->mIsModerator = speaker_it->second["is_moderator"]; -				speakerp->mModeratorMutedText = -					speaker_it->second["mutes"]["text"]; -				// Fire event only if moderator changed -				if ( is_moderator != speakerp->mIsModerator ) -				{ -					LL_DEBUGS("Speakers") << "Speaker " << agent_id << (is_moderator ? "is now" : "no longer is") << " a moderator" << LL_ENDL; -					fireEvent(new LLSpeakerUpdateModeratorEvent(speakerp), "update_moderator"); -				} -			} -		} -	} -	else if ( speakers.has("agents" ) && speakers["agents"].isArray() ) -	{ -		//older, more decprecated way.  Need here for -		//using older version of servers -		LLSD::array_const_iterator speaker_it; -		for(speaker_it = speakers["agents"].beginArray(); -			speaker_it != speakers["agents"].endArray(); -			++speaker_it) -		{ -			const LLUUID agent_id = (*speaker_it).asUUID(); - -			LLPointer<LLSpeaker> speakerp = setSpeaker( -				agent_id, -				LLStringUtil::null, -				LLSpeaker::STATUS_TEXT_ONLY); -		} -	} -} - -void LLIMSpeakerMgr::updateSpeakers(const LLSD& update) -{ -	if ( !update.isMap() ) return; - -	if ( update.has("agent_updates") && update["agent_updates"].isMap() ) -	{ -		LLSD::map_const_iterator update_it; -		for( -			update_it = update["agent_updates"].beginMap(); -			update_it != update["agent_updates"].endMap(); -			++update_it) -		{ -			LLUUID agent_id(update_it->first); -			LLPointer<LLSpeaker> speakerp = findSpeaker(agent_id); - -			LLSD agent_data = update_it->second; - -			if (agent_data.isMap() && agent_data.has("transition")) -			{ -				if (agent_data["transition"].asString() == "LEAVE") -				{ -					setSpeakerNotInChannel(speakerp); -				} -				else if (agent_data["transition"].asString() == "ENTER") -				{ -					// add or update speaker -					speakerp = setSpeaker(agent_id); -				} -				else -				{ -					LL_WARNS() << "bad membership list update from 'agent_updates' for agent " << agent_id << ", transition " << ll_print_sd(agent_data["transition"]) << LL_ENDL; -				} -			} - -			if (speakerp.isNull()) continue; - -			// should have a valid speaker from this point on -			if (agent_data.isMap() && agent_data.has("info")) -			{ -				LLSD agent_info = agent_data["info"]; - -				if (agent_info.has("is_moderator")) -				{ -					bool is_moderator = speakerp->mIsModerator; -					speakerp->mIsModerator = agent_info["is_moderator"]; -					// Fire event only if moderator changed -					if ( is_moderator != speakerp->mIsModerator ) -					{ -						LL_DEBUGS("Speakers") << "Speaker " << agent_id << (is_moderator ? "is now" : "no longer is") << " a moderator" << LL_ENDL; -						fireEvent(new LLSpeakerUpdateModeratorEvent(speakerp), "update_moderator"); -					} -				} - -				if (agent_info.has("mutes")) -				{ -					speakerp->mModeratorMutedText = agent_info["mutes"]["text"]; -				} -			} -		} -	} -	else if ( update.has("updates") && update["updates"].isMap() ) -	{ -		LLSD::map_const_iterator update_it; -		for ( -			update_it = update["updates"].beginMap(); -			update_it != update["updates"].endMap(); -			++update_it) -		{ -			LLUUID agent_id(update_it->first); -			LLPointer<LLSpeaker> speakerp = findSpeaker(agent_id); - -			std::string agent_transition = update_it->second.asString(); -			if (agent_transition == "LEAVE") -			{ -				setSpeakerNotInChannel(speakerp); -			} -			else if ( agent_transition == "ENTER") -			{ -				// add or update speaker -				speakerp = setSpeaker(agent_id); -			} -			else -			{ -				LL_WARNS() << "bad membership list update from 'updates' for agent " << agent_id << ", transition " << agent_transition << LL_ENDL; -			} -		} -	} -} - -void LLIMSpeakerMgr::toggleAllowTextChat(const LLUUID& speaker_id) -{ -	LLPointer<LLSpeaker> speakerp = findSpeaker(speaker_id); -	if (!speakerp) return; - -	std::string url = gAgent.getRegionCapability("ChatSessionRequest"); -	LLSD data; -	data["method"] = "mute update"; -	data["session-id"] = getSessionID(); -	data["params"] = LLSD::emptyMap(); -	data["params"]["agent_id"] = speaker_id; -	data["params"]["mute_info"] = LLSD::emptyMap(); -	//current value represents ability to type, so invert -	data["params"]["mute_info"]["text"] = !speakerp->mModeratorMutedText; - -    LLCoros::instance().launch("LLIMSpeakerMgr::moderationActionCoro", -        boost::bind(&LLIMSpeakerMgr::moderationActionCoro, this, url, data)); -} - -void LLIMSpeakerMgr::moderateVoiceParticipant(const LLUUID& avatar_id, bool unmute) -{ -	LLPointer<LLSpeaker> speakerp = findSpeaker(avatar_id); -	if (!speakerp) return; - -	// *NOTE: mantipov: probably this condition will be incorrect when avatar will be blocked for -	// text chat via moderation (LLSpeaker::mModeratorMutedText == true) -	bool is_in_voice = speakerp->mStatus <= LLSpeaker::STATUS_VOICE_ACTIVE || speakerp->mStatus == LLSpeaker::STATUS_MUTED; - -	// do not send voice moderation changes for avatars not in voice channel -	if (!is_in_voice) return; - -	std::string url = gAgent.getRegionCapability("ChatSessionRequest"); -	LLSD data; -	data["method"] = "mute update"; -	data["session-id"] = getSessionID(); -	data["params"] = LLSD::emptyMap(); -	data["params"]["agent_id"] = avatar_id; -	data["params"]["mute_info"] = LLSD::emptyMap(); -	data["params"]["mute_info"]["voice"] = !unmute; - -    LLCoros::instance().launch("LLIMSpeakerMgr::moderationActionCoro", -        boost::bind(&LLIMSpeakerMgr::moderationActionCoro, this, url, data)); -} - -void LLIMSpeakerMgr::moderationActionCoro(std::string url, LLSD action) -{ -    LLCore::HttpRequest::policy_t httpPolicy(LLCore::HttpRequest::DEFAULT_POLICY_ID); -    LLCoreHttpUtil::HttpCoroutineAdapter::ptr_t -        httpAdapter(new LLCoreHttpUtil::HttpCoroutineAdapter("moderationActionCoro", httpPolicy)); -    LLCore::HttpRequest::ptr_t httpRequest(new LLCore::HttpRequest); -    LLCore::HttpOptions::ptr_t httpOpts = LLCore::HttpOptions::ptr_t(new LLCore::HttpOptions); - -    httpOpts->setWantHeaders(true); - -    LLUUID sessionId = action["session-id"]; - -    LLSD result = httpAdapter->postAndSuspend(httpRequest, url, action, httpOpts); - -    LLSD httpResults = result[LLCoreHttpUtil::HttpCoroutineAdapter::HTTP_RESULTS]; -    LLCore::HttpStatus status = LLCoreHttpUtil::HttpCoroutineAdapter::getStatusFromLLSD(httpResults); - -    if (!status) -    { -        if (gIMMgr) -        { -            //403 == you're not a mod -            //should be disabled if you're not a moderator -            if (status == LLCore::HttpStatus(HTTP_FORBIDDEN)) -            { -                gIMMgr->showSessionEventError( -                    "mute", -                    "not_a_mod_error", -                    sessionId); -            } -            else -            { -                gIMMgr->showSessionEventError( -                    "mute", -                    "generic_request_error", -                    sessionId); -            } -        } -        return; -    } -} - -void LLIMSpeakerMgr::moderateVoiceAllParticipants( bool unmute_everyone ) -{ -	if (mVoiceModerated == !unmute_everyone) -	{ -		// session already in requested state. Just force participants which do not match it. -		forceVoiceModeratedMode(mVoiceModerated); -	} -	else -	{ -		// otherwise set moderated mode for a whole session. -		moderateVoiceSession(getSessionID(), !unmute_everyone); -	} -} - -void LLIMSpeakerMgr::processSessionUpdate(const LLSD& session_update) -{ -	if (session_update.has("moderated_mode") && -		session_update["moderated_mode"].has("voice")) -	{ -		mVoiceModerated = session_update["moderated_mode"]["voice"]; -	} -} - -void LLIMSpeakerMgr::moderateVoiceSession(const LLUUID& session_id, bool disallow_voice) -{ -	std::string url = gAgent.getRegionCapability("ChatSessionRequest"); -	LLSD data; -	data["method"] = "session update"; -	data["session-id"] = session_id; -	data["params"] = LLSD::emptyMap(); - -	data["params"]["update_info"] = LLSD::emptyMap(); - -	data["params"]["update_info"]["moderated_mode"] = LLSD::emptyMap(); -	data["params"]["update_info"]["moderated_mode"]["voice"] = disallow_voice; - -    LLCoros::instance().launch("LLIMSpeakerMgr::moderationActionCoro", -        boost::bind(&LLIMSpeakerMgr::moderationActionCoro, this, url, data)); -} - -void LLIMSpeakerMgr::forceVoiceModeratedMode(bool should_be_muted) -{ -	for (speaker_map_t::iterator speaker_it = mSpeakers.begin(); speaker_it != mSpeakers.end(); ++speaker_it) -	{ -		LLUUID speaker_id = speaker_it->first; -		LLSpeaker* speakerp = speaker_it->second; - -		// participant does not match requested state -		if (should_be_muted != (bool)speakerp->mModeratorMutedVoice) -		{ -			moderateVoiceParticipant(speaker_id, !should_be_muted); -		} -	} -} - -// -// LLActiveSpeakerMgr -// - -LLActiveSpeakerMgr::LLActiveSpeakerMgr() : LLSpeakerMgr(NULL) -{ -} - -void LLActiveSpeakerMgr::updateSpeakerList() -{ -	// point to whatever the current voice channel is -	mVoiceChannel = LLVoiceChannel::getCurrentVoiceChannel(); - -	// always populate from active voice channel -	if (LLVoiceChannel::getCurrentVoiceChannel() != mVoiceChannel) //MA: seems this is always false -	{ -		LL_DEBUGS("Speakers") << "Removed all speakers" << LL_ENDL; -		fireEvent(new LLSpeakerListChangeEvent(this, LLUUID::null), "clear"); -		mSpeakers.clear(); -		mSpeakersSorted.clear(); -		mVoiceChannel = LLVoiceChannel::getCurrentVoiceChannel(); -		mSpeakerDelayRemover->removeAllTimers(); -	} -	LLSpeakerMgr::updateSpeakerList(); - -	// clean up text only speakers -	for (speaker_map_t::iterator speaker_it = mSpeakers.begin(); speaker_it != mSpeakers.end(); ++speaker_it) -	{ -		LLSpeaker* speakerp = speaker_it->second; -		if (speakerp->mStatus == LLSpeaker::STATUS_TEXT_ONLY) -		{ -			// automatically flag text only speakers for removal -			speakerp->mStatus = LLSpeaker::STATUS_NOT_IN_CHANNEL; -		} -	} - -} - - - -// -// LLLocalSpeakerMgr -// - -LLLocalSpeakerMgr::LLLocalSpeakerMgr() : LLSpeakerMgr(LLVoiceChannelProximal::getInstance()) -{ -} - -LLLocalSpeakerMgr::~LLLocalSpeakerMgr () -{ -} - -void LLLocalSpeakerMgr::updateSpeakerList() -{ -	// pull speakers from voice channel -	LLSpeakerMgr::updateSpeakerList(); - -	if (gDisconnected)//the world is cleared. -	{ -		return ; -	} - -	// pick up non-voice speakers in chat range -	uuid_vec_t avatar_ids; -	std::vector<LLVector3d> positions; -	LLWorld::getInstance()->getAvatars(&avatar_ids, &positions, gAgent.getPositionGlobal(), CHAT_NORMAL_RADIUS); -	for(U32 i=0; i<avatar_ids.size(); i++) -	{ -		setSpeaker(avatar_ids[i]); -	} - -	// check if text only speakers have moved out of chat range -	for (speaker_map_t::iterator speaker_it = mSpeakers.begin(); speaker_it != mSpeakers.end(); ++speaker_it) -	{ -		LLUUID speaker_id = speaker_it->first; -		LLPointer<LLSpeaker> speakerp = speaker_it->second; -		if (speakerp.notNull() && speakerp->mStatus == LLSpeaker::STATUS_TEXT_ONLY) -		{ -			LLVOAvatar* avatarp = (LLVOAvatar*)gObjectList.findObject(speaker_id); -			if (!avatarp || dist_vec_squared(avatarp->getPositionAgent(), gAgent.getPositionAgent()) > CHAT_NORMAL_RADIUS * CHAT_NORMAL_RADIUS) -			{ -				setSpeakerNotInChannel(speakerp); -			} -		} -	} -} +/**
 + * @file llspeakers.cpp
 + * @brief Management interface for muting and controlling volume of residents currently speaking
 + *
 + * $LicenseInfo:firstyear=2005&license=viewerlgpl$
 + * Second Life Viewer Source Code
 + * Copyright (C) 2010, Linden Research, 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
 + *
 + * Linden Research, Inc., 945 Battery Street, San Francisco, CA  94111  USA
 + * $/LicenseInfo$
 + */
 +
 +#include "llviewerprecompiledheaders.h"
 +
 +#include "llspeakers.h"
 +
 +#include "llagent.h"
 +#include "llavatarnamecache.h"
 +#include "llappviewer.h"
 +#include "llimview.h"
 +#include "llgroupmgr.h"
 +#include "llsdutil.h"
 +#include "lluicolortable.h"
 +#include "llviewerobjectlist.h"
 +#include "llviewerregion.h"
 +#include "llvoavatar.h"
 +#include "llworld.h"
 +#include "llcorehttputil.h"
 +
 +extern LLControlGroup gSavedSettings;
 +
 +const LLColor4 INACTIVE_COLOR(0.3f, 0.3f, 0.3f, 0.5f);
 +const LLColor4 ACTIVE_COLOR(0.5f, 0.5f, 0.5f, 1.f);
 +
 +LLSpeaker::LLSpeaker(const LLUUID& id, const std::string& name, const ESpeakerType type) :
 +    mStatus(LLSpeaker::STATUS_TEXT_ONLY),
 +    mLastSpokeTime(0.f),
 +    mSpeechVolume(0.f),
 +    mHasSpoken(false),
 +    mHasLeftCurrentCall(false),
 +    mDotColor(LLColor4::white),
 +    mID(id),
 +    mTyping(false),
 +    mSortIndex(0),
 +    mType(type),
 +    mIsModerator(false),
 +    mModeratorMutedVoice(false),
 +    mModeratorMutedText(false)
 +{
 +    if (name.empty() && type == SPEAKER_AGENT)
 +    {
 +        lookupName();
 +    }
 +    else
 +    {
 +        mDisplayName = name;
 +    }
 +}
 +
 +
 +void LLSpeaker::lookupName()
 +{
 +    if (mDisplayName.empty())
 +    {
 +        LLAvatarNameCache::get(mID, boost::bind(&LLSpeaker::onNameCache, this, _1, _2)); // todo: can be group???
 +    }
 +}
 +
 +void LLSpeaker::onNameCache(const LLUUID& id, const LLAvatarName& av_name)
 +{
 +    mDisplayName = av_name.getUserName();
 +}
 +
 +bool LLSpeaker::isInVoiceChannel()
 +{
 +    return mStatus <= LLSpeaker::STATUS_VOICE_ACTIVE || mStatus == LLSpeaker::STATUS_MUTED;
 +}
 +
 +LLSpeakerUpdateSpeakerEvent::LLSpeakerUpdateSpeakerEvent(LLSpeaker* source)
 +: LLEvent(source, "Speaker update speaker event"),
 +  mSpeakerID (source->mID)
 +{
 +}
 +
 +LLSD LLSpeakerUpdateSpeakerEvent::getValue()
 +{
 +    LLSD ret;
 +    ret["id"] = mSpeakerID;
 +    return ret;
 +}
 +
 +LLSpeakerUpdateModeratorEvent::LLSpeakerUpdateModeratorEvent(LLSpeaker* source)
 +: LLEvent(source, "Speaker add moderator event"),
 +  mSpeakerID (source->mID),
 +  mIsModerator (source->mIsModerator)
 +{
 +}
 +
 +LLSD LLSpeakerUpdateModeratorEvent::getValue()
 +{
 +    LLSD ret;
 +    ret["id"] = mSpeakerID;
 +    ret["is_moderator"] = mIsModerator;
 +    return ret;
 +}
 +
 +LLSpeakerTextModerationEvent::LLSpeakerTextModerationEvent(LLSpeaker* source)
 +: LLEvent(source, "Speaker text moderation event")
 +{
 +}
 +
 +LLSD LLSpeakerTextModerationEvent::getValue()
 +{
 +    return std::string("text");
 +}
 +
 +
 +LLSpeakerVoiceModerationEvent::LLSpeakerVoiceModerationEvent(LLSpeaker* source)
 +: LLEvent(source, "Speaker voice moderation event")
 +{
 +}
 +
 +LLSD LLSpeakerVoiceModerationEvent::getValue()
 +{
 +    return std::string("voice");
 +}
 +
 +LLSpeakerListChangeEvent::LLSpeakerListChangeEvent(LLSpeakerMgr* source, const LLUUID& speaker_id)
 +: LLEvent(source, "Speaker added/removed from speaker mgr"),
 +  mSpeakerID(speaker_id)
 +{
 +}
 +
 +LLSD LLSpeakerListChangeEvent::getValue()
 +{
 +    return mSpeakerID;
 +}
 +
 +// helper sort class
 +struct LLSortRecentSpeakers
 +{
 +    bool operator()(const LLPointer<LLSpeaker> lhs, const LLPointer<LLSpeaker> rhs) const;
 +};
 +
 +bool LLSortRecentSpeakers::operator()(const LLPointer<LLSpeaker> lhs, const LLPointer<LLSpeaker> rhs) const
 +{
 +    // Sort first on status
 +    if (lhs->mStatus != rhs->mStatus)
 +    {
 +        return (lhs->mStatus < rhs->mStatus);
 +    }
 +
 +    // and then on last speaking time
 +    if(lhs->mLastSpokeTime != rhs->mLastSpokeTime)
 +    {
 +        return (lhs->mLastSpokeTime > rhs->mLastSpokeTime);
 +    }
 +
 +    // and finally (only if those are both equal), on name.
 +    return( lhs->mDisplayName.compare(rhs->mDisplayName) < 0 );
 +}
 +
 +LLSpeakerActionTimer::LLSpeakerActionTimer(action_callback_t action_cb, F32 action_period, const LLUUID& speaker_id)
 +: LLEventTimer(action_period)
 +, mActionCallback(action_cb)
 +, mSpeakerId(speaker_id)
 +{
 +}
 +
 +bool LLSpeakerActionTimer::tick()
 +{
 +    if (mActionCallback)
 +    {
 +        return (bool)mActionCallback(mSpeakerId);
 +    }
 +    return true;
 +}
 +
 +void LLSpeakerActionTimer::unset()
 +{
 +    mActionCallback = 0;
 +}
 +
 +LLSpeakersDelayActionsStorage::LLSpeakersDelayActionsStorage(LLSpeakerActionTimer::action_callback_t action_cb, F32 action_delay)
 +: mActionCallback(action_cb)
 +, mActionDelay(action_delay)
 +{
 +}
 +
 +LLSpeakersDelayActionsStorage::~LLSpeakersDelayActionsStorage()
 +{
 +    removeAllTimers();
 +}
 +
 +void LLSpeakersDelayActionsStorage::setActionTimer(const LLUUID& speaker_id)
 +{
 +    bool not_found = true;
 +    if (mActionTimersMap.size() > 0)
 +    {
 +        not_found = mActionTimersMap.find(speaker_id) == mActionTimersMap.end();
 +    }
 +
 +    // If there is already a started timer for the passed UUID don't do anything.
 +    if (not_found)
 +    {
 +        // Starting a timer to remove an participant after delay is completed
 +        mActionTimersMap.insert(LLSpeakerActionTimer::action_value_t(speaker_id,
 +            new LLSpeakerActionTimer(
 +                boost::bind(&LLSpeakersDelayActionsStorage::onTimerActionCallback, this, _1),
 +                mActionDelay, speaker_id)));
 +    }
 +}
 +
 +void LLSpeakersDelayActionsStorage::unsetActionTimer(const LLUUID& speaker_id)
 +{
 +    if (mActionTimersMap.size() == 0) return;
 +
 +    LLSpeakerActionTimer::action_timer_iter_t it_speaker = mActionTimersMap.find(speaker_id);
 +
 +    if (it_speaker != mActionTimersMap.end())
 +    {
 +        it_speaker->second->unset();
 +        mActionTimersMap.erase(it_speaker);
 +    }
 +}
 +
 +void LLSpeakersDelayActionsStorage::removeAllTimers()
 +{
 +    LLSpeakerActionTimer::action_timer_iter_t iter = mActionTimersMap.begin();
 +    for (; iter != mActionTimersMap.end(); ++iter)
 +    {
 +        delete iter->second;
 +    }
 +    mActionTimersMap.clear();
 +}
 +
 +bool LLSpeakersDelayActionsStorage::onTimerActionCallback(const LLUUID& speaker_id)
 +{
 +    unsetActionTimer(speaker_id);
 +
 +    if (mActionCallback)
 +    {
 +        mActionCallback(speaker_id);
 +    }
 +
 +    return true;
 +}
 +
 +bool LLSpeakersDelayActionsStorage::isTimerStarted(const LLUUID& speaker_id)
 +{
 +    return (mActionTimersMap.size() > 0) && (mActionTimersMap.find(speaker_id) != mActionTimersMap.end());
 +}
 +
 +//
 +// LLSpeakerMgr
 +//
 +
 +LLSpeakerMgr::LLSpeakerMgr(LLVoiceChannel* channelp) :
 +    mVoiceChannel(channelp),
 +    mVoiceModerated(false),
 +    mModerateModeHandledFirstTime(false),
 +    mSpeakerListUpdated(false)
 +{
 +    mGetListTime.reset();
 +    static LLUICachedControl<F32> remove_delay ("SpeakerParticipantRemoveDelay", 10.0);
 +
 +    mSpeakerDelayRemover = new LLSpeakersDelayActionsStorage(boost::bind(&LLSpeakerMgr::removeSpeaker, this, _1), remove_delay);
 +}
 +
 +LLSpeakerMgr::~LLSpeakerMgr()
 +{
 +    delete mSpeakerDelayRemover;
 +}
 +
 +LLPointer<LLSpeaker> LLSpeakerMgr::setSpeaker(const LLUUID& id, const std::string& name, LLSpeaker::ESpeakerStatus status, LLSpeaker::ESpeakerType type)
 +{
 +    LLUUID session_id = getSessionID();
 +    if (id.isNull() || (id == session_id))
 +    {
 +        return NULL;
 +    }
 +
 +    LLPointer<LLSpeaker> speakerp;
 +    if (mSpeakers.find(id) == mSpeakers.end())
 +    {
 +        speakerp = new LLSpeaker(id, name, type);
 +        speakerp->mStatus = status;
 +        mSpeakers.insert(std::make_pair(speakerp->mID, speakerp));
 +        mSpeakersSorted.push_back(speakerp);
 +        LL_DEBUGS("Speakers") << "Added speaker " << id << LL_ENDL;
 +        fireEvent(new LLSpeakerListChangeEvent(this, speakerp->mID), "add");
 +    }
 +    else
 +    {
 +        speakerp = findSpeaker(id);
 +        if (speakerp.notNull())
 +        {
 +            // keep highest priority status (lowest value) instead of overriding current value
 +            speakerp->mStatus = llmin(speakerp->mStatus, status);
 +            // RN: due to a weird behavior where IMs from attached objects come from the wearer's agent_id
 +            // we need to override speakers that we think are objects when we find out they are really
 +            // residents
 +            if (type == LLSpeaker::SPEAKER_AGENT)
 +            {
 +                speakerp->mType = LLSpeaker::SPEAKER_AGENT;
 +                speakerp->lookupName();
 +            }
 +        }
 +        else
 +        {
 +            LL_WARNS("Speakers") << "Speaker " << id << " not found" << LL_ENDL;
 +        }
 +    }
 +
 +    mSpeakerDelayRemover->unsetActionTimer(speakerp->mID);
 +    return speakerp;
 +}
 +
 +// *TODO: Once way to request the current voice channel moderation mode is implemented
 +// this method with related code should be removed.
 +/*
 + Initializes "moderate_mode" of voice session on first join.
 +
 + This is WORKAROUND because a way to request the current voice channel moderation mode exists
 + but is not implemented in viewer yet. See EXT-6937.
 +*/
 +void LLSpeakerMgr::initVoiceModerateMode()
 +{
 +    if (!mModerateModeHandledFirstTime && (mVoiceChannel && mVoiceChannel->isActive()))
 +    {
 +        LLPointer<LLSpeaker> speakerp;
 +
 +        if (mSpeakers.find(gAgentID) != mSpeakers.end())
 +        {
 +            speakerp = mSpeakers[gAgentID];
 +        }
 +
 +        if (speakerp.notNull())
 +        {
 +            mVoiceModerated = speakerp->mModeratorMutedVoice;
 +            mModerateModeHandledFirstTime = true;
 +        }
 +    }
 +}
 +
 +void LLSpeakerMgr::update(bool resort_ok)
 +{
 +    if (!LLVoiceClient::getInstance())
 +    {
 +        return;
 +    }
 +
 +    LLColor4 speaking_color = LLUIColorTable::instance().getColor("SpeakingColor");
 +    LLColor4 overdriven_color = LLUIColorTable::instance().getColor("OverdrivenColor");
 +
 +    if(resort_ok) // only allow list changes when user is not interacting with it
 +    {
 +        updateSpeakerList();
 +    }
 +
 +    // update status of all current speakers
 +    bool voice_channel_active = (!mVoiceChannel && LLVoiceClient::getInstance()->inProximalChannel()) || (mVoiceChannel && mVoiceChannel->isActive());
 +    for (speaker_map_t::iterator speaker_it = mSpeakers.begin(); speaker_it != mSpeakers.end(); speaker_it++)
 +    {
 +        LLUUID speaker_id = speaker_it->first;
 +        LLSpeaker* speakerp = speaker_it->second;
 +
 +        if (voice_channel_active && LLVoiceClient::getInstance()->getVoiceEnabled(speaker_id))
 +        {
 +            speakerp->mSpeechVolume = LLVoiceClient::getInstance()->getCurrentPower(speaker_id);
 +            bool moderator_muted_voice = LLVoiceClient::getInstance()->getIsModeratorMuted(speaker_id);
 +            if (moderator_muted_voice != speakerp->mModeratorMutedVoice)
 +            {
 +                speakerp->mModeratorMutedVoice = moderator_muted_voice;
 +                LL_DEBUGS("Speakers") << (speakerp->mModeratorMutedVoice? "Muted" : "Umuted") << " speaker " << speaker_id<< LL_ENDL;
 +                speakerp->fireEvent(new LLSpeakerVoiceModerationEvent(speakerp));
 +            }
 +
 +            if (LLVoiceClient::getInstance()->getOnMuteList(speaker_id) || speakerp->mModeratorMutedVoice)
 +            {
 +                speakerp->mStatus = LLSpeaker::STATUS_MUTED;
 +            }
 +            else if (LLVoiceClient::getInstance()->getIsSpeaking(speaker_id))
 +            {
 +                // reset inactivity expiration
 +                if (speakerp->mStatus != LLSpeaker::STATUS_SPEAKING)
 +                {
 +                    speakerp->mLastSpokeTime = mSpeechTimer.getElapsedTimeF32();
 +                    speakerp->mHasSpoken = true;
 +                    fireEvent(new LLSpeakerUpdateSpeakerEvent(speakerp), "update_speaker");
 +                }
 +                speakerp->mStatus = LLSpeaker::STATUS_SPEAKING;
 +                // interpolate between active color and full speaking color based on power of speech output
 +                speakerp->mDotColor = speaking_color;
 +                if (speakerp->mSpeechVolume > LLVoiceClient::OVERDRIVEN_POWER_LEVEL)
 +                {
 +                    speakerp->mDotColor = overdriven_color;
 +                }
 +            }
 +            else
 +            {
 +                speakerp->mSpeechVolume = 0.f;
 +                speakerp->mDotColor = ACTIVE_COLOR;
 +
 +                if (speakerp->mHasSpoken)
 +                {
 +                    // have spoken once, not currently speaking
 +                    speakerp->mStatus = LLSpeaker::STATUS_HAS_SPOKEN;
 +                }
 +                else
 +                {
 +                    // default state for being in voice channel
 +                    speakerp->mStatus = LLSpeaker::STATUS_VOICE_ACTIVE;
 +                }
 +            }
 +        }
 +        // speaker no longer registered in voice channel, demote to text only
 +        else if (speakerp->mStatus != LLSpeaker::STATUS_NOT_IN_CHANNEL)
 +        {
 +            if(speakerp->mType == LLSpeaker::SPEAKER_EXTERNAL)
 +            {
 +                // external speakers should be timed out when they leave the voice channel (since they only exist via SLVoice)
 +                speakerp->mStatus = LLSpeaker::STATUS_NOT_IN_CHANNEL;
 +            }
 +            else
 +            {
 +                speakerp->mStatus = LLSpeaker::STATUS_TEXT_ONLY;
 +                speakerp->mSpeechVolume = 0.f;
 +                speakerp->mDotColor = ACTIVE_COLOR;
 +            }
 +        }
 +    }
 +
 +    if(resort_ok)  // only allow list changes when user is not interacting with it
 +    {
 +        // sort by status then time last spoken
 +        std::sort(mSpeakersSorted.begin(), mSpeakersSorted.end(), LLSortRecentSpeakers());
 +    }
 +
 +    // for recent speakers who are not currently speaking, show "recent" color dot for most recent
 +    // fading to "active" color
 +
 +    S32 recent_speaker_count = 0;
 +    S32 sort_index = 0;
 +    speaker_list_t::iterator sorted_speaker_it;
 +    for(sorted_speaker_it = mSpeakersSorted.begin();
 +        sorted_speaker_it != mSpeakersSorted.end(); ++sorted_speaker_it)
 +    {
 +        LLPointer<LLSpeaker> speakerp = *sorted_speaker_it;
 +
 +        // color code recent speakers who are not currently speaking
 +        if (speakerp->mStatus == LLSpeaker::STATUS_HAS_SPOKEN)
 +        {
 +            speakerp->mDotColor = lerp(speaking_color, ACTIVE_COLOR, clamp_rescale((F32)recent_speaker_count, -2.f, 3.f, 0.f, 1.f));
 +            recent_speaker_count++;
 +        }
 +
 +        // stuff sort ordinal into speaker so the ui can sort by this value
 +        speakerp->mSortIndex = sort_index++;
 +    }
 +}
 +
 +void LLSpeakerMgr::updateSpeakerList()
 +{
 +    // Are we bound to the currently active voice channel?
 +    if ((!mVoiceChannel && LLVoiceClient::getInstance()->inProximalChannel()) || (mVoiceChannel && mVoiceChannel->isActive()))
 +    {
 +        std::set<LLUUID> participants;
 +        LLVoiceClient::getInstance()->getParticipantList(participants);
 +        // If we are, add all voice client participants to our list of known speakers
 +        for (std::set<LLUUID>::iterator participant_it = participants.begin(); participant_it != participants.end(); ++participant_it)
 +        {
 +                setSpeaker(*participant_it,
 +                           LLVoiceClient::getInstance()->getDisplayName(*participant_it),
 +                           LLSpeaker::STATUS_VOICE_ACTIVE,
 +                           (LLVoiceClient::getInstance()->isParticipantAvatar(*participant_it)?LLSpeaker::SPEAKER_AGENT:LLSpeaker::SPEAKER_EXTERNAL));
 +        }
 +    }
 +    else
 +    {
 +        // If not, check if the list is empty, except if it's Nearby Chat (session_id NULL).
 +        LLUUID session_id = getSessionID();
 +        if (!session_id.isNull() && !mSpeakerListUpdated)
 +        {
 +            // If the list is empty, we update it with whatever we have locally so that it doesn't stay empty too long.
 +            // *TODO: Fix the server side code that sometimes forgets to send back the list of participants after a chat started.
 +            // (IOW, fix why we get no ChatterBoxSessionAgentListUpdates message after the initial ChatterBoxSessionStartReply)
 +            LLIMModel::LLIMSession* session = LLIMModel::getInstance()->findIMSession(session_id);
 +            if (session->isGroupSessionType() && (mSpeakers.size() <= 1))
 +            {
 +                // For groups, we need to hit the group manager.
 +                // Note: The session uuid and the group uuid are actually one and the same. If that was to change, this will fail.
 +                LLGroupMgrGroupData* gdatap = LLGroupMgr::getInstance()->getGroupData(session_id);
 +
 +                if (gdatap && gdatap->isMemberDataComplete() && !gdatap->mMembers.empty())
 +                {
 +                    // Add group members when we get the complete list (note: can take a while before we get that list)
 +                    LLGroupMgrGroupData::member_list_t::iterator member_it = gdatap->mMembers.begin();
 +                    const S32 load_group_max_members = gSavedSettings.getS32("ChatLoadGroupMaxMembers");
 +                    S32 updated = 0;
 +                    while (member_it != gdatap->mMembers.end())
 +                    {
 +                        LLGroupMemberData* member = member_it->second;
 +                        LLUUID id = member_it->first;
 +                        // Add only members who are online and not already in the list
 +                        if ((member->getOnlineStatus() == "Online") && (mSpeakers.find(id) == mSpeakers.end()))
 +                        {
 +                            LLPointer<LLSpeaker> speakerp = setSpeaker(id, "", LLSpeaker::STATUS_VOICE_ACTIVE, LLSpeaker::SPEAKER_AGENT);
 +                            speakerp->mIsModerator = ((member->getAgentPowers() & GP_SESSION_MODERATOR) == GP_SESSION_MODERATOR);
 +                            updated++;
 +                        }
 +                        ++member_it;
 +                        // Limit the number of "manually updated" participants to a reasonable number to avoid severe fps drop
 +                        // *TODO : solve the perf issue of having several hundreds of widgets in the conversation list
 +                        if (updated >= load_group_max_members)
 +                            break;
 +                    }
 +                    mSpeakerListUpdated = true;
 +                }
 +            }
 +            else if (mSpeakers.size() == 0)
 +            {
 +                // For all other session type (ad-hoc, P2P), we use the initial participants targets list
 +                for (uuid_vec_t::iterator it = session->mInitialTargetIDs.begin();it!=session->mInitialTargetIDs.end();++it)
 +                {
 +                    // Add buddies if they are on line, add any other avatar.
 +                    if (!LLAvatarTracker::instance().isBuddy(*it) || LLAvatarTracker::instance().isBuddyOnline(*it))
 +                    {
 +                        setSpeaker(*it, "", LLSpeaker::STATUS_VOICE_ACTIVE, LLSpeaker::SPEAKER_AGENT);
 +                    }
 +                }
 +                mSpeakerListUpdated = true;
 +            }
 +            else
 +            {
 +                // The list has been updated the normal way (i.e. by a ChatterBoxSessionAgentListUpdates received from the server)
 +                mSpeakerListUpdated = true;
 +            }
 +        }
 +    }
 +    // Always add the current agent (it has to be there...). Will do nothing if already there.
 +    setSpeaker(gAgentID, "", LLSpeaker::STATUS_VOICE_ACTIVE, LLSpeaker::SPEAKER_AGENT);
 +}
 +
 +void LLSpeakerMgr::setSpeakerNotInChannel(LLPointer<LLSpeaker> speakerp)
 +{
 +    if  (speakerp.notNull())
 +    {
 +        speakerp->mStatus = LLSpeaker::STATUS_NOT_IN_CHANNEL;
 +        speakerp->mDotColor = INACTIVE_COLOR;
 +        mSpeakerDelayRemover->setActionTimer(speakerp->mID);
 +    }
 +}
 +
 +bool LLSpeakerMgr::removeSpeaker(const LLUUID& speaker_id)
 +{
 +    mSpeakers.erase(speaker_id);
 +
 +    speaker_list_t::iterator sorted_speaker_it = mSpeakersSorted.begin();
 +
 +    for(; sorted_speaker_it != mSpeakersSorted.end(); ++sorted_speaker_it)
 +    {
 +        if (speaker_id == (*sorted_speaker_it)->mID)
 +        {
 +            mSpeakersSorted.erase(sorted_speaker_it);
 +            break;
 +        }
 +    }
 +
 +    LL_DEBUGS("Speakers") << "Removed speaker " << speaker_id << LL_ENDL;
 +    fireEvent(new LLSpeakerListChangeEvent(this, speaker_id), "remove");
 +
 +    update(true);
 +
 +    return false;
 +}
 +
 +LLPointer<LLSpeaker> LLSpeakerMgr::findSpeaker(const LLUUID& speaker_id)
 +{
 +    //In some conditions map causes crash if it is empty(Windows only), adding check (EK)
 +    if (mSpeakers.size() == 0)
 +        return NULL;
 +    speaker_map_t::iterator found_it = mSpeakers.find(speaker_id);
 +    if (found_it == mSpeakers.end())
 +    {
 +        return NULL;
 +    }
 +    return found_it->second;
 +}
 +
 +void LLSpeakerMgr::getSpeakerList(speaker_list_t* speaker_list, bool include_text)
 +{
 +    speaker_list->clear();
 +    for (speaker_map_t::iterator speaker_it = mSpeakers.begin(); speaker_it != mSpeakers.end(); ++speaker_it)
 +    {
 +        LLPointer<LLSpeaker> speakerp = speaker_it->second;
 +        // what about text only muted or inactive?
 +        if (include_text || speakerp->mStatus != LLSpeaker::STATUS_TEXT_ONLY)
 +        {
 +            speaker_list->push_back(speakerp);
 +        }
 +    }
 +}
 +
 +const LLUUID LLSpeakerMgr::getSessionID()
 +{
 +    return mVoiceChannel->getSessionID();
 +}
 +
 +bool LLSpeakerMgr::isSpeakerToBeRemoved(const LLUUID& speaker_id)
 +{
 +    return mSpeakerDelayRemover && mSpeakerDelayRemover->isTimerStarted(speaker_id);
 +}
 +
 +void LLSpeakerMgr::setSpeakerTyping(const LLUUID& speaker_id, bool typing)
 +{
 +    LLPointer<LLSpeaker> speakerp = findSpeaker(speaker_id);
 +    if (speakerp.notNull())
 +    {
 +        speakerp->mTyping = typing;
 +    }
 +}
 +
 +// speaker has chatted via either text or voice
 +void LLSpeakerMgr::speakerChatted(const LLUUID& speaker_id)
 +{
 +    LLPointer<LLSpeaker> speakerp = findSpeaker(speaker_id);
 +    if (speakerp.notNull())
 +    {
 +        speakerp->mLastSpokeTime = mSpeechTimer.getElapsedTimeF32();
 +        speakerp->mHasSpoken = true;
 +        fireEvent(new LLSpeakerUpdateSpeakerEvent(speakerp), "update_speaker");
 +    }
 +}
 +
 +bool LLSpeakerMgr::isVoiceActive()
 +{
 +    // mVoiceChannel = NULL means current voice channel, whatever it is
 +    return LLVoiceClient::getInstance()->voiceEnabled() && mVoiceChannel && mVoiceChannel->isActive();
 +}
 +
 +
 +//
 +// LLIMSpeakerMgr
 +//
 +LLIMSpeakerMgr::LLIMSpeakerMgr(LLVoiceChannel* channel) : LLSpeakerMgr(channel)
 +{
 +}
 +
 +void LLIMSpeakerMgr::updateSpeakerList()
 +{
 +    // don't do normal updates which are pulled from voice channel
 +    // rely on user list reported by sim
 +
 +    // We need to do this to allow PSTN callers into group chats to show in the list.
 +    LLSpeakerMgr::updateSpeakerList();
 +
 +    return;
 +}
 +
 +void LLIMSpeakerMgr::setSpeakers(const LLSD& speakers)
 +{
 +    if ( !speakers.isMap() ) return;
 +
 +    if ( speakers.has("agent_info") && speakers["agent_info"].isMap() )
 +    {
 +        LLSD::map_const_iterator speaker_it;
 +        for(speaker_it = speakers["agent_info"].beginMap();
 +            speaker_it != speakers["agent_info"].endMap();
 +            ++speaker_it)
 +        {
 +            LLUUID agent_id(speaker_it->first);
 +
 +            LLPointer<LLSpeaker> speakerp = setSpeaker(
 +                agent_id,
 +                LLStringUtil::null,
 +                LLSpeaker::STATUS_TEXT_ONLY);
 +
 +            if ( speaker_it->second.isMap() )
 +            {
 +                bool is_moderator = speakerp->mIsModerator;
 +                speakerp->mIsModerator = speaker_it->second["is_moderator"];
 +                speakerp->mModeratorMutedText =
 +                    speaker_it->second["mutes"]["text"];
 +                // Fire event only if moderator changed
 +                if ( is_moderator != speakerp->mIsModerator )
 +                {
 +                    LL_DEBUGS("Speakers") << "Speaker " << agent_id << (is_moderator ? "is now" : "no longer is") << " a moderator" << LL_ENDL;
 +                    fireEvent(new LLSpeakerUpdateModeratorEvent(speakerp), "update_moderator");
 +                }
 +            }
 +        }
 +    }
 +    else if ( speakers.has("agents" ) && speakers["agents"].isArray() )
 +    {
 +        //older, more decprecated way.  Need here for
 +        //using older version of servers
 +        LLSD::array_const_iterator speaker_it;
 +        for(speaker_it = speakers["agents"].beginArray();
 +            speaker_it != speakers["agents"].endArray();
 +            ++speaker_it)
 +        {
 +            const LLUUID agent_id = (*speaker_it).asUUID();
 +
 +            LLPointer<LLSpeaker> speakerp = setSpeaker(
 +                agent_id,
 +                LLStringUtil::null,
 +                LLSpeaker::STATUS_TEXT_ONLY);
 +        }
 +    }
 +}
 +
 +void LLIMSpeakerMgr::updateSpeakers(const LLSD& update)
 +{
 +    if ( !update.isMap() ) return;
 +
 +    if ( update.has("agent_updates") && update["agent_updates"].isMap() )
 +    {
 +        LLSD::map_const_iterator update_it;
 +        for(
 +            update_it = update["agent_updates"].beginMap();
 +            update_it != update["agent_updates"].endMap();
 +            ++update_it)
 +        {
 +            LLUUID agent_id(update_it->first);
 +            LLPointer<LLSpeaker> speakerp = findSpeaker(agent_id);
 +
 +            LLSD agent_data = update_it->second;
 +
 +            if (agent_data.isMap() && agent_data.has("transition"))
 +            {
 +                if (agent_data["transition"].asString() == "LEAVE")
 +                {
 +                    setSpeakerNotInChannel(speakerp);
 +                }
 +                else if (agent_data["transition"].asString() == "ENTER")
 +                {
 +                    // add or update speaker
 +                    speakerp = setSpeaker(agent_id);
 +                }
 +                else
 +                {
 +                    LL_WARNS() << "bad membership list update from 'agent_updates' for agent " << agent_id << ", transition " << ll_print_sd(agent_data["transition"]) << LL_ENDL;
 +                }
 +            }
 +
 +            if (speakerp.isNull()) continue;
 +
 +            // should have a valid speaker from this point on
 +            if (agent_data.isMap() && agent_data.has("info"))
 +            {
 +                LLSD agent_info = agent_data["info"];
 +
 +                if (agent_info.has("is_moderator"))
 +                {
 +                    bool is_moderator = speakerp->mIsModerator;
 +                    speakerp->mIsModerator = agent_info["is_moderator"];
 +                    // Fire event only if moderator changed
 +                    if ( is_moderator != speakerp->mIsModerator )
 +                    {
 +                        LL_DEBUGS("Speakers") << "Speaker " << agent_id << (is_moderator ? "is now" : "no longer is") << " a moderator" << LL_ENDL;
 +                        fireEvent(new LLSpeakerUpdateModeratorEvent(speakerp), "update_moderator");
 +                    }
 +                }
 +
 +                if (agent_info.has("mutes"))
 +                {
 +                    speakerp->mModeratorMutedText = agent_info["mutes"]["text"];
 +                }
 +            }
 +        }
 +    }
 +    else if ( update.has("updates") && update["updates"].isMap() )
 +    {
 +        LLSD::map_const_iterator update_it;
 +        for (
 +            update_it = update["updates"].beginMap();
 +            update_it != update["updates"].endMap();
 +            ++update_it)
 +        {
 +            LLUUID agent_id(update_it->first);
 +            LLPointer<LLSpeaker> speakerp = findSpeaker(agent_id);
 +
 +            std::string agent_transition = update_it->second.asString();
 +            if (agent_transition == "LEAVE")
 +            {
 +                setSpeakerNotInChannel(speakerp);
 +            }
 +            else if ( agent_transition == "ENTER")
 +            {
 +                // add or update speaker
 +                speakerp = setSpeaker(agent_id);
 +            }
 +            else
 +            {
 +                LL_WARNS() << "bad membership list update from 'updates' for agent " << agent_id << ", transition " << agent_transition << LL_ENDL;
 +            }
 +        }
 +    }
 +}
 +
 +void LLIMSpeakerMgr::toggleAllowTextChat(const LLUUID& speaker_id)
 +{
 +    LLPointer<LLSpeaker> speakerp = findSpeaker(speaker_id);
 +    if (!speakerp) return;
 +
 +    std::string url = gAgent.getRegionCapability("ChatSessionRequest");
 +    LLSD data;
 +    data["method"] = "mute update";
 +    data["session-id"] = getSessionID();
 +    data["params"] = LLSD::emptyMap();
 +    data["params"]["agent_id"] = speaker_id;
 +    data["params"]["mute_info"] = LLSD::emptyMap();
 +    //current value represents ability to type, so invert
 +    data["params"]["mute_info"]["text"] = !speakerp->mModeratorMutedText;
 +
 +    LLCoros::instance().launch("LLIMSpeakerMgr::moderationActionCoro",
 +        boost::bind(&LLIMSpeakerMgr::moderationActionCoro, this, url, data));
 +}
 +
 +void LLIMSpeakerMgr::moderateVoiceParticipant(const LLUUID& avatar_id, bool unmute)
 +{
 +    LLPointer<LLSpeaker> speakerp = findSpeaker(avatar_id);
 +    if (!speakerp) return;
 +
 +    // *NOTE: mantipov: probably this condition will be incorrect when avatar will be blocked for
 +    // text chat via moderation (LLSpeaker::mModeratorMutedText == true)
 +    bool is_in_voice = speakerp->mStatus <= LLSpeaker::STATUS_VOICE_ACTIVE || speakerp->mStatus == LLSpeaker::STATUS_MUTED;
 +
 +    // do not send voice moderation changes for avatars not in voice channel
 +    if (!is_in_voice) return;
 +
 +    std::string url = gAgent.getRegionCapability("ChatSessionRequest");
 +    LLSD data;
 +    data["method"] = "mute update";
 +    data["session-id"] = getSessionID();
 +    data["params"] = LLSD::emptyMap();
 +    data["params"]["agent_id"] = avatar_id;
 +    data["params"]["mute_info"] = LLSD::emptyMap();
 +    data["params"]["mute_info"]["voice"] = !unmute;
 +
 +    LLCoros::instance().launch("LLIMSpeakerMgr::moderationActionCoro",
 +        boost::bind(&LLIMSpeakerMgr::moderationActionCoro, this, url, data));
 +}
 +
 +void LLIMSpeakerMgr::moderationActionCoro(std::string url, LLSD action)
 +{
 +    LLCore::HttpRequest::policy_t httpPolicy(LLCore::HttpRequest::DEFAULT_POLICY_ID);
 +    LLCoreHttpUtil::HttpCoroutineAdapter::ptr_t
 +        httpAdapter(new LLCoreHttpUtil::HttpCoroutineAdapter("moderationActionCoro", httpPolicy));
 +    LLCore::HttpRequest::ptr_t httpRequest(new LLCore::HttpRequest);
 +    LLCore::HttpOptions::ptr_t httpOpts = LLCore::HttpOptions::ptr_t(new LLCore::HttpOptions);
 +
 +    httpOpts->setWantHeaders(true);
 +
 +    LLUUID sessionId = action["session-id"];
 +
 +    LLSD result = httpAdapter->postAndSuspend(httpRequest, url, action, httpOpts);
 +
 +    LLSD httpResults = result[LLCoreHttpUtil::HttpCoroutineAdapter::HTTP_RESULTS];
 +    LLCore::HttpStatus status = LLCoreHttpUtil::HttpCoroutineAdapter::getStatusFromLLSD(httpResults);
 +
 +    if (!status)
 +    {
 +        if (gIMMgr)
 +        {
 +            //403 == you're not a mod
 +            //should be disabled if you're not a moderator
 +            if (status == LLCore::HttpStatus(HTTP_FORBIDDEN))
 +            {
 +                gIMMgr->showSessionEventError(
 +                    "mute",
 +                    "not_a_mod_error",
 +                    sessionId);
 +            }
 +            else
 +            {
 +                gIMMgr->showSessionEventError(
 +                    "mute",
 +                    "generic_request_error",
 +                    sessionId);
 +            }
 +        }
 +        return;
 +    }
 +}
 +
 +void LLIMSpeakerMgr::moderateVoiceAllParticipants( bool unmute_everyone )
 +{
 +    if (mVoiceModerated == !unmute_everyone)
 +    {
 +        // session already in requested state. Just force participants which do not match it.
 +        forceVoiceModeratedMode(mVoiceModerated);
 +    }
 +    else
 +    {
 +        // otherwise set moderated mode for a whole session.
 +        moderateVoiceSession(getSessionID(), !unmute_everyone);
 +    }
 +}
 +
 +void LLIMSpeakerMgr::processSessionUpdate(const LLSD& session_update)
 +{
 +    if (session_update.has("moderated_mode") &&
 +        session_update["moderated_mode"].has("voice"))
 +    {
 +        mVoiceModerated = session_update["moderated_mode"]["voice"];
 +    }
 +}
 +
 +void LLIMSpeakerMgr::moderateVoiceSession(const LLUUID& session_id, bool disallow_voice)
 +{
 +    std::string url = gAgent.getRegionCapability("ChatSessionRequest");
 +    LLSD data;
 +    data["method"] = "session update";
 +    data["session-id"] = session_id;
 +    data["params"] = LLSD::emptyMap();
 +
 +    data["params"]["update_info"] = LLSD::emptyMap();
 +
 +    data["params"]["update_info"]["moderated_mode"] = LLSD::emptyMap();
 +    data["params"]["update_info"]["moderated_mode"]["voice"] = disallow_voice;
 +
 +    LLCoros::instance().launch("LLIMSpeakerMgr::moderationActionCoro",
 +        boost::bind(&LLIMSpeakerMgr::moderationActionCoro, this, url, data));
 +}
 +
 +void LLIMSpeakerMgr::forceVoiceModeratedMode(bool should_be_muted)
 +{
 +    for (speaker_map_t::iterator speaker_it = mSpeakers.begin(); speaker_it != mSpeakers.end(); ++speaker_it)
 +    {
 +        LLUUID speaker_id = speaker_it->first;
 +        LLSpeaker* speakerp = speaker_it->second;
 +
 +        // participant does not match requested state
 +        if (should_be_muted != (bool)speakerp->mModeratorMutedVoice)
 +        {
 +            moderateVoiceParticipant(speaker_id, !should_be_muted);
 +        }
 +    }
 +}
 +
 +//
 +// LLActiveSpeakerMgr
 +//
 +
 +LLActiveSpeakerMgr::LLActiveSpeakerMgr() : LLSpeakerMgr(NULL)
 +{
 +}
 +
 +void LLActiveSpeakerMgr::updateSpeakerList()
 +{
 +    // point to whatever the current voice channel is
 +    mVoiceChannel = LLVoiceChannel::getCurrentVoiceChannel();
 +
 +    // always populate from active voice channel
 +    if (LLVoiceChannel::getCurrentVoiceChannel() != mVoiceChannel) //MA: seems this is always false
 +    {
 +        LL_DEBUGS("Speakers") << "Removed all speakers" << LL_ENDL;
 +        fireEvent(new LLSpeakerListChangeEvent(this, LLUUID::null), "clear");
 +        mSpeakers.clear();
 +        mSpeakersSorted.clear();
 +        mVoiceChannel = LLVoiceChannel::getCurrentVoiceChannel();
 +        mSpeakerDelayRemover->removeAllTimers();
 +    }
 +    LLSpeakerMgr::updateSpeakerList();
 +
 +    // clean up text only speakers
 +    for (speaker_map_t::iterator speaker_it = mSpeakers.begin(); speaker_it != mSpeakers.end(); ++speaker_it)
 +    {
 +        LLSpeaker* speakerp = speaker_it->second;
 +        if (speakerp->mStatus == LLSpeaker::STATUS_TEXT_ONLY)
 +        {
 +            // automatically flag text only speakers for removal
 +            speakerp->mStatus = LLSpeaker::STATUS_NOT_IN_CHANNEL;
 +        }
 +    }
 +
 +}
 +
 +
 +
 +//
 +// LLLocalSpeakerMgr
 +//
 +
 +LLLocalSpeakerMgr::LLLocalSpeakerMgr() : LLSpeakerMgr(LLVoiceChannelProximal::getInstance())
 +{
 +}
 +
 +LLLocalSpeakerMgr::~LLLocalSpeakerMgr ()
 +{
 +}
 +
 +void LLLocalSpeakerMgr::updateSpeakerList()
 +{
 +    // pull speakers from voice channel
 +    LLSpeakerMgr::updateSpeakerList();
 +
 +    if (gDisconnected)//the world is cleared.
 +    {
 +        return ;
 +    }
 +
 +    // pick up non-voice speakers in chat range
 +    uuid_vec_t avatar_ids;
 +    std::vector<LLVector3d> positions;
 +    LLWorld::getInstance()->getAvatars(&avatar_ids, &positions, gAgent.getPositionGlobal(), CHAT_NORMAL_RADIUS);
 +    for(U32 i=0; i<avatar_ids.size(); i++)
 +    {
 +        setSpeaker(avatar_ids[i]);
 +    }
 +
 +    // check if text only speakers have moved out of chat range
 +    for (speaker_map_t::iterator speaker_it = mSpeakers.begin(); speaker_it != mSpeakers.end(); ++speaker_it)
 +    {
 +        LLUUID speaker_id = speaker_it->first;
 +        LLPointer<LLSpeaker> speakerp = speaker_it->second;
 +        if (speakerp.notNull() && speakerp->mStatus == LLSpeaker::STATUS_TEXT_ONLY)
 +        {
 +            LLVOAvatar* avatarp = (LLVOAvatar*)gObjectList.findObject(speaker_id);
 +            if (!avatarp || dist_vec_squared(avatarp->getPositionAgent(), gAgent.getPositionAgent()) > CHAT_NORMAL_RADIUS * CHAT_NORMAL_RADIUS)
 +            {
 +                setSpeakerNotInChannel(speakerp);
 +            }
 +        }
 +    }
 +}
  | 
