From 86921724c438e31957bc803695054bba80645df4 Mon Sep 17 00:00:00 2001
From: Igor Borovkov <iborovkov@productengine.com>
Date: Wed, 28 Oct 2009 18:31:43 +0200
Subject: IM refactoring: moved speakers management stuff in its own files
 llspeakers.* from dying llfloateractivespeakers. * Decresed number of
 llfloateractivespeakers.h includes to a minimum.

--HG--
branch : product-engine
---
 indra/newview/CMakeLists.txt        |   2 +
 indra/newview/llagent.cpp           |   2 +-
 indra/newview/llimpanel.cpp         |   1 +
 indra/newview/llimview.h            |   2 +-
 indra/newview/llnearbychatbar.h     |   2 +-
 indra/newview/llparticipantlist.cpp |   2 +-
 indra/newview/llspeakers.cpp        | 639 ++++++++++++++++++++++++++++++++++++
 indra/newview/llspeakers.h          | 172 ++++++++++
 indra/newview/llviewermessage.cpp   |   1 -
 indra/newview/llvoicechannel.h      |   1 +
 10 files changed, 819 insertions(+), 5 deletions(-)
 create mode 100644 indra/newview/llspeakers.cpp
 create mode 100644 indra/newview/llspeakers.h

diff --git a/indra/newview/CMakeLists.txt b/indra/newview/CMakeLists.txt
index d81ce0c4db..5210ff66ed 100644
--- a/indra/newview/CMakeLists.txt
+++ b/indra/newview/CMakeLists.txt
@@ -371,6 +371,7 @@ set(viewer_SOURCE_FILES
     llsky.cpp
     llslurl.cpp
     llspatialpartition.cpp
+    llspeakers.cpp
     llsplitbutton.cpp
     llsprite.cpp
     llstartup.cpp
@@ -848,6 +849,7 @@ set(viewer_HEADER_FILES
     llsky.h
     llslurl.h
     llspatialpartition.h
+    llspeakers.h
     llsplitbutton.h
     llsprite.h
     llstartup.h
diff --git a/indra/newview/llagent.cpp b/indra/newview/llagent.cpp
index 5800db482f..75a72e5b17 100644
--- a/indra/newview/llagent.cpp
+++ b/indra/newview/llagent.cpp
@@ -42,7 +42,7 @@
 #include "lldrawable.h"
 #include "llfirstuse.h"
 #include "llfloaterreg.h"
-#include "llfloateractivespeakers.h"
+#include "llspeakers.h"
 #include "llfloatercamera.h"
 #include "llfloatercustomize.h"
 
diff --git a/indra/newview/llimpanel.cpp b/indra/newview/llimpanel.cpp
index 211e657a76..77ee90f681 100644
--- a/indra/newview/llimpanel.cpp
+++ b/indra/newview/llimpanel.cpp
@@ -54,6 +54,7 @@
 #include "llconsole.h"
 #include "llgroupactions.h"
 #include "llfloater.h"
+#include "llfloateractivespeakers.h"
 #include "llfloatercall.h"
 #include "llavataractions.h"
 #include "llimview.h"
diff --git a/indra/newview/llimview.h b/indra/newview/llimview.h
index 80b54d7ef0..8c2ca149a6 100644
--- a/indra/newview/llimview.h
+++ b/indra/newview/llimview.h
@@ -34,7 +34,7 @@
 #define LL_LLIMVIEW_H
 
 #include "lldarray.h"
-#include "llfloateractivespeakers.h" //for LLIMSpeakerMgr
+#include "llspeakers.h" //for LLIMSpeakerMgr
 #include "llimpanel.h" //for voice channels
 #include "llmodaldialog.h"
 #include "llinstantmessage.h"
diff --git a/indra/newview/llnearbychatbar.h b/indra/newview/llnearbychatbar.h
index d495a10193..06204e6367 100644
--- a/indra/newview/llnearbychatbar.h
+++ b/indra/newview/llnearbychatbar.h
@@ -40,7 +40,7 @@
 #include "llchiclet.h"
 #include "llvoiceclient.h"
 #include "lloutputmonitorctrl.h"
-#include "llfloateractivespeakers.h"
+#include "llspeakers.h"
 
 class LLGestureComboBox
 	: public LLComboBox
diff --git a/indra/newview/llparticipantlist.cpp b/indra/newview/llparticipantlist.cpp
index a8c66f08ac..e97eb1df2b 100644
--- a/indra/newview/llparticipantlist.cpp
+++ b/indra/newview/llparticipantlist.cpp
@@ -34,7 +34,7 @@
 
 #include "llparticipantlist.h"
 #include "llavatarlist.h"
-#include "llfloateractivespeakers.h"
+#include "llspeakers.h"
 
 //LLParticipantList retrieves add, clear and remove events and updates view accordingly 
 LLParticipantList::LLParticipantList(LLSpeakerMgr* data_source, LLAvatarList* avatar_list):
diff --git a/indra/newview/llspeakers.cpp b/indra/newview/llspeakers.cpp
new file mode 100644
index 0000000000..2341fcfc6d
--- /dev/null
+++ b/indra/newview/llspeakers.cpp
@@ -0,0 +1,639 @@
+/** 
+ * @file llspeakers.cpp
+ * @brief Management interface for muting and controlling volume of residents currently speaking
+ *
+ * $LicenseInfo:firstyear=2005&license=viewergpl$
+ * 
+ * Copyright (c) 2005-2009, Linden Research, Inc.
+ * 
+ * Second Life Viewer Source Code
+ * The source code in this file ("Source Code") is provided by Linden Lab
+ * to you under the terms of the GNU General Public License, version 2.0
+ * ("GPL"), unless you have obtained a separate licensing agreement
+ * ("Other License"), formally executed by you and Linden Lab.  Terms of
+ * the GPL can be found in doc/GPL-license.txt in this distribution, or
+ * online at http://secondlifegrid.net/programs/open_source/licensing/gplv2
+ * 
+ * There are special exceptions to the terms and conditions of the GPL as
+ * it is applied to this Source Code. View the full text of the exception
+ * in the file doc/FLOSS-exception.txt in this software distribution, or
+ * online at
+ * http://secondlifegrid.net/programs/open_source/licensing/flossexception
+ * 
+ * By copying, modifying or distributing this software, you acknowledge
+ * that you have read and understood your obligations described above,
+ * and agree to abide by those obligations.
+ * 
+ * ALL LINDEN LAB SOURCE CODE IS PROVIDED "AS IS." LINDEN LAB MAKES NO
+ * WARRANTIES, EXPRESS, IMPLIED OR OTHERWISE, REGARDING ITS ACCURACY,
+ * COMPLETENESS OR PERFORMANCE.
+ * $/LicenseInfo$
+ */
+
+#include "llviewerprecompiledheaders.h"
+
+#include "llspeakers.h"
+
+#include "llagent.h"
+#include "llappviewer.h"
+#include "llmutelist.h"
+#include "llsdutil.h"
+#include "lluicolortable.h"
+#include "llviewerobjectlist.h"
+#include "llvoavatar.h"
+#include "llworld.h"
+
+const F32 SPEAKER_TIMEOUT = 10.f; // seconds of not being on voice channel before removed from list of active speakers
+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;
+	}
+
+	gVoiceClient->setUserVolume(id, LLMuteList::getInstance()->getSavedResidentVolume(id));
+
+	mActivityTimer.resetWithExpiry(SPEAKER_TIMEOUT);
+}
+
+
+void LLSpeaker::lookupName()
+{
+	gCacheName->get(mID, FALSE, boost::bind(&LLSpeaker::onAvatarNameLookup, this, _1, _2, _3, _4));
+}
+
+void LLSpeaker::onAvatarNameLookup(const LLUUID& id, const std::string& first, const std::string& last, BOOL is_group)
+{
+	mDisplayName = first + " " + last;
+}
+
+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 );
+}
+
+
+//
+// LLSpeakerMgr
+//
+
+LLSpeakerMgr::LLSpeakerMgr(LLVoiceChannel* channelp) : 
+	mVoiceChannel(channelp)
+{
+}
+
+LLSpeakerMgr::~LLSpeakerMgr()
+{
+}
+
+LLPointer<LLSpeaker> LLSpeakerMgr::setSpeaker(const LLUUID& id, const std::string& name, LLSpeaker::ESpeakerStatus status, LLSpeaker::ESpeakerType type)
+{
+	if (id.isNull()) 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);
+		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);
+			speakerp->mActivityTimer.resetWithExpiry(SPEAKER_TIMEOUT);
+			// 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();
+			}
+		}
+	}
+
+	return speakerp;
+}
+
+void LLSpeakerMgr::update(BOOL resort_ok)
+{
+	if (!gVoiceClient)
+	{
+		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 && gVoiceClient->inProximalChannel()) || (mVoiceChannel && mVoiceChannel->isActive());
+	for (speaker_map_t::iterator speaker_it = mSpeakers.begin(); speaker_it != mSpeakers.end();)
+	{
+		LLUUID speaker_id = speaker_it->first;
+		LLSpeaker* speakerp = speaker_it->second;
+		
+		speaker_map_t::iterator  cur_speaker_it = speaker_it++;
+
+		if (voice_channel_active && gVoiceClient->getVoiceEnabled(speaker_id))
+		{
+			speakerp->mSpeechVolume = gVoiceClient->getCurrentPower(speaker_id);
+			BOOL moderator_muted_voice = gVoiceClient->getIsModeratorMuted(speaker_id);
+			if (moderator_muted_voice != speakerp->mModeratorMutedVoice)
+			{
+				speakerp->mModeratorMutedVoice = moderator_muted_voice;
+				speakerp->fireEvent(new LLSpeakerVoiceModerationEvent(speakerp));
+			}
+
+			if (gVoiceClient->getOnMuteList(speaker_id) || speakerp->mModeratorMutedVoice)
+			{
+				speakerp->mStatus = LLSpeaker::STATUS_MUTED;
+			}
+			else if (gVoiceClient->getIsSpeaking(speaker_id))
+			{
+				// reset inactivity expiration
+				if (speakerp->mStatus != LLSpeaker::STATUS_SPEAKING)
+				{
+					speakerp->mLastSpokeTime = mSpeechTimer.getElapsedTimeF32();
+					speakerp->mHasSpoken = TRUE;
+				}
+				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(); )
+	{
+		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++;
+
+		// remove speakers that have been gone too long
+		if (speakerp->mStatus == LLSpeaker::STATUS_NOT_IN_CHANNEL && speakerp->mActivityTimer.hasExpired())
+		{
+			fireEvent(new LLSpeakerListChangeEvent(this, speakerp->mID), "remove");
+
+			mSpeakers.erase(speakerp->mID);
+			sorted_speaker_it = mSpeakersSorted.erase(sorted_speaker_it);
+		}
+		else
+		{
+			++sorted_speaker_it;
+		}
+	}
+}
+
+void LLSpeakerMgr::updateSpeakerList()
+{
+	// are we bound to the currently active voice channel?
+	if ((!mVoiceChannel && gVoiceClient->inProximalChannel()) || (mVoiceChannel && mVoiceChannel->isActive()))
+	{
+		LLVoiceClient::participantMap* participants = gVoiceClient->getParticipantList();
+		if(participants)
+		{
+			LLVoiceClient::participantMap::iterator participant_it;
+
+			// add new participants to our list of known speakers
+			for (participant_it = participants->begin(); participant_it != participants->end(); ++participant_it)
+			{
+				LLVoiceClient::participantState* participantp = participant_it->second;
+				setSpeaker(participantp->mAvatarID, participantp->mDisplayName, LLSpeaker::STATUS_VOICE_ACTIVE, (participantp->isAvatar()?LLSpeaker::SPEAKER_AGENT:LLSpeaker::SPEAKER_EXTERNAL));
+			}
+		}
+	}
+}
+
+LLPointer<LLSpeaker> LLSpeakerMgr::findSpeaker(const LLUUID& speaker_id)
+{
+	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(); 
+}
+
+
+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;
+	}
+}
+
+BOOL LLSpeakerMgr::isVoiceActive()
+{
+	// mVoiceChannel = NULL means current voice channel, whatever it is
+	return LLVoiceClient::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() )
+			{
+				speakerp->mIsModerator = speaker_it->second["is_moderator"];
+				speakerp->mModeratorMutedText =
+					speaker_it->second["mutes"]["text"];
+			}
+		}
+	}
+	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" && speakerp.notNull())
+				{
+					speakerp->mStatus = LLSpeaker::STATUS_NOT_IN_CHANNEL;
+					speakerp->mDotColor = INACTIVE_COLOR;
+					speakerp->mActivityTimer.resetWithExpiry(SPEAKER_TIMEOUT);
+				}
+				else if (agent_data["transition"].asString() == "ENTER")
+				{
+					// add or update speaker
+					speakerp = setSpeaker(agent_id);
+				}
+				else
+				{
+					llwarns << "bad membership list update " << ll_print_sd(agent_data["transition"]) << llendl;
+				}
+			}
+
+			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"))
+				{
+					speakerp->mIsModerator = agent_info["is_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" && speakerp.notNull())
+			{
+				speakerp->mStatus = LLSpeaker::STATUS_NOT_IN_CHANNEL;
+				speakerp->mDotColor = INACTIVE_COLOR;
+				speakerp->mActivityTimer.resetWithExpiry(SPEAKER_TIMEOUT);
+			}
+			else if ( agent_transition == "ENTER")
+			{
+				// add or update speaker
+				speakerp = setSpeaker(agent_id);
+			}
+			else
+			{
+				llwarns << "bad membership list update "
+						<< agent_transition << llendl;
+			}
+		}
+	}
+}
+
+
+//
+// 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)
+	{
+		fireEvent(new LLSpeakerListChangeEvent(this, LLUUID::null), "clear");
+		mSpeakers.clear();
+		mSpeakersSorted.clear();
+		mVoiceChannel = LLVoiceChannel::getCurrentVoiceChannel();
+	}
+	LLSpeakerMgr::updateSpeakerList();
+
+	// clean up text only speakers
+	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 (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
+	std::vector<LLUUID> 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;
+		LLSpeaker* speakerp = speaker_it->second;
+		if (speakerp->mStatus == LLSpeaker::STATUS_TEXT_ONLY)
+		{
+			LLVOAvatar* avatarp = (LLVOAvatar*)gObjectList.findObject(speaker_id);
+			if (!avatarp || dist_vec(avatarp->getPositionAgent(), gAgent.getPositionAgent()) > CHAT_NORMAL_RADIUS)
+			{
+				speakerp->mStatus = LLSpeaker::STATUS_NOT_IN_CHANNEL;
+				speakerp->mDotColor = INACTIVE_COLOR;
+				speakerp->mActivityTimer.resetWithExpiry(SPEAKER_TIMEOUT);
+			}
+		}
+	}
+}
diff --git a/indra/newview/llspeakers.h b/indra/newview/llspeakers.h
new file mode 100644
index 0000000000..e0f22bff4f
--- /dev/null
+++ b/indra/newview/llspeakers.h
@@ -0,0 +1,172 @@
+/** 
+ * @file llspeakers.h
+ * @brief Management interface for muting and controlling volume of residents currently speaking
+ *
+ * $LicenseInfo:firstyear=2005&license=viewergpl$
+ * 
+ * Copyright (c) 2005-2009, Linden Research, Inc.
+ * 
+ * Second Life Viewer Source Code
+ * The source code in this file ("Source Code") is provided by Linden Lab
+ * to you under the terms of the GNU General Public License, version 2.0
+ * ("GPL"), unless you have obtained a separate licensing agreement
+ * ("Other License"), formally executed by you and Linden Lab.  Terms of
+ * the GPL can be found in doc/GPL-license.txt in this distribution, or
+ * online at http://secondlifegrid.net/programs/open_source/licensing/gplv2
+ * 
+ * There are special exceptions to the terms and conditions of the GPL as
+ * it is applied to this Source Code. View the full text of the exception
+ * in the file doc/FLOSS-exception.txt in this software distribution, or
+ * online at
+ * http://secondlifegrid.net/programs/open_source/licensing/flossexception
+ * 
+ * By copying, modifying or distributing this software, you acknowledge
+ * that you have read and understood your obligations described above,
+ * and agree to abide by those obligations.
+ * 
+ * ALL LINDEN LAB SOURCE CODE IS PROVIDED "AS IS." LINDEN LAB MAKES NO
+ * WARRANTIES, EXPRESS, IMPLIED OR OTHERWISE, REGARDING ITS ACCURACY,
+ * COMPLETENESS OR PERFORMANCE.
+ * $/LicenseInfo$
+ */
+
+#ifndef LL_LLSPEAKERS_H
+#define LL_LLSPEAKERS_H
+
+#include "llevent.h"
+#include "llspeakers.h"
+#include "llvoicechannel.h"
+
+class LLSpeakerMgr;
+
+// data for a given participant in a voice channel
+class LLSpeaker : public LLRefCount, public LLOldEvents::LLObservable, public LLHandleProvider<LLSpeaker>, public boost::signals2::trackable
+{
+public:
+	typedef enum e_speaker_type
+	{
+		SPEAKER_AGENT,
+		SPEAKER_OBJECT,
+		SPEAKER_EXTERNAL	// Speaker that doesn't map to an avatar or object (i.e. PSTN caller in a group)
+	} ESpeakerType;
+
+	typedef enum e_speaker_status
+	{
+		STATUS_SPEAKING,
+		STATUS_HAS_SPOKEN,
+		STATUS_VOICE_ACTIVE,
+		STATUS_TEXT_ONLY,
+		STATUS_NOT_IN_CHANNEL,
+		STATUS_MUTED
+	} ESpeakerStatus;
+
+
+	LLSpeaker(const LLUUID& id, const std::string& name = LLStringUtil::null, const ESpeakerType type = SPEAKER_AGENT);
+	~LLSpeaker() {};
+	void lookupName();
+
+	void onAvatarNameLookup(const LLUUID& id, const std::string& first, const std::string& last, BOOL is_group);
+
+	ESpeakerStatus	mStatus;			// current activity status in speech group
+	F32				mLastSpokeTime;		// timestamp when this speaker last spoke
+	F32				mSpeechVolume;		// current speech amplitude (timea average rms amplitude?)
+	std::string		mDisplayName;		// cache user name for this speaker
+	LLFrameTimer	mActivityTimer;	// time out speakers when they are not part of current voice channel
+	BOOL			mHasSpoken;			// has this speaker said anything this session?
+	BOOL			mHasLeftCurrentCall;	// has this speaker left the current voice call?
+	LLColor4		mDotColor;
+	LLUUID			mID;
+	BOOL			mTyping;
+	S32				mSortIndex;
+	ESpeakerType	mType;
+	BOOL			mIsModerator;
+	BOOL			mModeratorMutedVoice;
+	BOOL			mModeratorMutedText;
+};
+
+class LLSpeakerTextModerationEvent : public LLOldEvents::LLEvent
+{
+public:
+	LLSpeakerTextModerationEvent(LLSpeaker* source);
+	/*virtual*/ LLSD getValue();
+};
+
+class LLSpeakerVoiceModerationEvent : public LLOldEvents::LLEvent
+{
+public:
+	LLSpeakerVoiceModerationEvent(LLSpeaker* source);
+	/*virtual*/ LLSD getValue();
+};
+
+class LLSpeakerListChangeEvent : public LLOldEvents::LLEvent
+{
+public:
+	LLSpeakerListChangeEvent(LLSpeakerMgr* source, const LLUUID& speaker_id);
+	/*virtual*/ LLSD getValue();
+
+private:
+	const LLUUID& mSpeakerID;
+};
+
+class LLSpeakerMgr : public LLOldEvents::LLObservable
+{
+public:
+	LLSpeakerMgr(LLVoiceChannel* channelp);
+	virtual ~LLSpeakerMgr();
+
+	LLPointer<LLSpeaker> findSpeaker(const LLUUID& avatar_id);
+	void update(BOOL resort_ok);
+	void setSpeakerTyping(const LLUUID& speaker_id, BOOL typing);
+	void speakerChatted(const LLUUID& speaker_id);
+	LLPointer<LLSpeaker> setSpeaker(const LLUUID& id, 
+					const std::string& name = LLStringUtil::null, 
+					LLSpeaker::ESpeakerStatus status = LLSpeaker::STATUS_TEXT_ONLY, 
+					LLSpeaker::ESpeakerType = LLSpeaker::SPEAKER_AGENT);
+
+	BOOL isVoiceActive();
+
+	typedef std::vector<LLPointer<LLSpeaker> > speaker_list_t;
+	void getSpeakerList(speaker_list_t* speaker_list, BOOL include_text);
+	LLVoiceChannel* getVoiceChannel() { return mVoiceChannel; }
+	const LLUUID getSessionID();
+
+protected:
+	virtual void updateSpeakerList();
+
+	typedef std::map<LLUUID, LLPointer<LLSpeaker> > speaker_map_t;
+	speaker_map_t		mSpeakers;
+
+	speaker_list_t		mSpeakersSorted;
+	LLFrameTimer		mSpeechTimer;
+	LLVoiceChannel*		mVoiceChannel;
+};
+
+class LLIMSpeakerMgr : public LLSpeakerMgr
+{
+public:
+	LLIMSpeakerMgr(LLVoiceChannel* channel);
+	
+	void updateSpeakers(const LLSD& update);
+	void setSpeakers(const LLSD& speakers);
+protected:
+	virtual void updateSpeakerList();
+};
+
+class LLActiveSpeakerMgr : public LLSpeakerMgr, public LLSingleton<LLActiveSpeakerMgr>
+{
+public:
+	LLActiveSpeakerMgr();
+protected:
+	virtual void updateSpeakerList();
+};
+
+class LLLocalSpeakerMgr : public LLSpeakerMgr, public LLSingleton<LLLocalSpeakerMgr>
+{
+public:
+	LLLocalSpeakerMgr();
+	~LLLocalSpeakerMgr ();
+protected:
+	virtual void updateSpeakerList();
+};
+
+#endif // LL_LLSPEAKERS_H
diff --git a/indra/newview/llviewermessage.cpp b/indra/newview/llviewermessage.cpp
index 791ec07349..a171d66d5c 100644
--- a/indra/newview/llviewermessage.cpp
+++ b/indra/newview/llviewermessage.cpp
@@ -72,7 +72,6 @@
 #include "llviewercontrol.h"
 #include "lldrawpool.h"
 #include "llfirstuse.h"
-#include "llfloateractivespeakers.h"
 #include "llfloateranimpreview.h"
 #include "llfloaterbuycurrency.h"
 #include "llfloaterbuyland.h"
diff --git a/indra/newview/llvoicechannel.h b/indra/newview/llvoicechannel.h
index 4a9100fba6..9966bdd5ab 100644
--- a/indra/newview/llvoicechannel.h
+++ b/indra/newview/llvoicechannel.h
@@ -33,6 +33,7 @@
 #ifndef LL_VOICECHANNEL_H
 #define LL_VOICECHANNEL_H
 
+#include "llhandle.h"
 #include "llvoiceclient.h"
 
 class LLPanel;
-- 
cgit v1.2.3