From 7bfb8ec0503d7c31d9241b2e95f103fa66b14e1e Mon Sep 17 00:00:00 2001
From: Mike Antipov <mantipov@productengine.com>
Date: Mon, 21 Dec 2009 20:42:34 +0200
Subject: [PATCH] Work on normal bug EXT-3434 	There is no difference between
 invited and left participants in a Group call (Voice Controls) -- implemented
 decorating of left participants in voice chat via italic font style (draft,
 harcoded)

--HG--
branch : product-engine
---
 indra/newview/llavatarlistitem.cpp  |  22 +++
 indra/newview/llavatarlistitem.h    |   1 +
 indra/newview/llcallfloater.cpp     | 235 +++++++++++++++++++++++++++-
 indra/newview/llcallfloater.h       |  35 ++++-
 indra/newview/llparticipantlist.cpp |  21 ---
 indra/newview/llparticipantlist.h   |   6 -
 6 files changed, 289 insertions(+), 31 deletions(-)

diff --git a/indra/newview/llavatarlistitem.cpp b/indra/newview/llavatarlistitem.cpp
index bbe5cdcc4a8..76dce622ed2 100644
--- a/indra/newview/llavatarlistitem.cpp
+++ b/indra/newview/llavatarlistitem.cpp
@@ -184,6 +184,28 @@ void LLAvatarListItem::setHighlight(const std::string& highlight)
 	setNameInternal(mAvatarName->getText(), mHighlihtSubstring = highlight);
 }
 
+void LLAvatarListItem::setStyle(const LLStyle::Params& new_style)
+{
+//	LLTextUtil::textboxSetHighlightedVal(mAvatarName, mAvatarNameStyle = new_style);
+
+	// Active group should be bold.
+	LLFontDescriptor new_desc(mAvatarName->getDefaultFont()->getFontDesc());
+
+	new_desc.setStyle(new_style.font()->getFontDesc().getStyle());
+	// *NOTE dzaporozhan
+	// On Windows LLFontGL::NORMAL will not remove LLFontGL::BOLD if font 
+	// is predefined as bold (SansSerifSmallBold, for example)
+//	new_desc.setStyle(active ? LLFontGL::BOLD : LLFontGL::NORMAL);
+	LLFontGL* new_font = LLFontGL::getFont(new_desc);
+
+//	
+	mAvatarNameStyle.font = new_font;
+
+	// *NOTE: You cannot set the style on a text box anymore, you must
+	// rebuild the text.  This will cause problems if the text contains
+	// hyperlinks, as their styles will be wrong.
+	mAvatarName->setText(mAvatarName->getText(), mAvatarNameStyle/* = new_style*/);
+}
 void LLAvatarListItem::setAvatarId(const LLUUID& id, bool ignore_status_changes)
 {
 	if (mAvatarId.notNull())
diff --git a/indra/newview/llavatarlistitem.h b/indra/newview/llavatarlistitem.h
index df8b04126e9..ad85d5fa878 100644
--- a/indra/newview/llavatarlistitem.h
+++ b/indra/newview/llavatarlistitem.h
@@ -73,6 +73,7 @@ class LLAvatarListItem : public LLPanel, public LLFriendObserver
 	void setOnline(bool online);
 	void setName(const std::string& name);
 	void setHighlight(const std::string& highlight);
+	void setStyle(const LLStyle::Params& new_style);
 	void setAvatarId(const LLUUID& id, bool ignore_status_changes = false);
 	void setLastInteractionTime(U32 secs_since);
 	//Show/hide profile/info btn, translating speaker indicator and avatar name coordinates accordingly
diff --git a/indra/newview/llcallfloater.cpp b/indra/newview/llcallfloater.cpp
index 2521bde8fa1..fe4f0c5525a 100644
--- a/indra/newview/llcallfloater.cpp
+++ b/indra/newview/llcallfloater.cpp
@@ -89,6 +89,7 @@ LLCallFloater::LLCallFloater(const LLSD& key)
 , mAgentPanel(NULL)
 , mSpeakingIndicator(NULL)
 , mIsModeratorMutedVoice(false)
+, mInitParticipantsVoiceState(false)
 {
 	mFactoryMap["non_avatar_caller"] = LLCallbackMap(create_non_avatar_caller, NULL);
 	LLVoiceClient::getInstance()->addObserver(this);
@@ -100,6 +101,8 @@ LLCallFloater::~LLCallFloater()
 	delete mPaticipants;
 	mPaticipants = NULL;
 
+	mAvatarListRefreshConnection.disconnect();
+
 	// Don't use LLVoiceClient::getInstance() here 
 	// singleton MAY have already been destroyed.
 	if(gVoiceClient)
@@ -114,6 +117,8 @@ BOOL LLCallFloater::postBuild()
 {
 	LLDockableFloater::postBuild();
 	mAvatarList = getChild<LLAvatarList>("speakers_list");
+	mAvatarListRefreshConnection = mAvatarList->setRefreshCompleteCallback(boost::bind(&LLCallFloater::onAvatarListRefreshed, this));
+
 	childSetAction("leave_call_btn", boost::bind(&LLCallFloater::leaveCall, this));
 
 	mNonAvatarCaller = getChild<LLNonAvatarCaller>("non_avatar_caller");
@@ -165,8 +170,7 @@ void LLCallFloater::onChange()
 {
 	if (NULL == mPaticipants) return;
 
-	mPaticipants->refreshVoiceState();
-
+	updateParticipantsVoiceState();
 }
 
 
@@ -291,7 +295,23 @@ void LLCallFloater::refreshPartisipantList()
 		{
 			mAvatarList->setNoItemsCommentText(getString("no_one_near"));
 		}
-		mPaticipants->refreshVoiceState();	
+
+		// we have to made delayed initialization of voice state of participant list.
+		// it will be performed after first LLAvatarList refreshing in the onAvatarListRefreshed().
+		mInitParticipantsVoiceState = true;
+	}
+}
+
+void LLCallFloater::onAvatarListRefreshed()
+{
+	if (mInitParticipantsVoiceState)
+	{
+		initParticipantsVoiceState();
+		mInitParticipantsVoiceState = false;
+	}
+	else
+	{
+		updateParticipantsVoiceState();
 	}
 }
 
@@ -393,4 +413,213 @@ void LLCallFloater::updateAgentModeratorState()
 	}
 	mAgentPanel->childSetValue("user_text", name);
 }
+
+void get_voice_participants_uuids(std::vector<LLUUID>& speakers_uuids)
+{
+	// Get a list of participants from VoiceClient
+	LLVoiceClient::participantMap *voice_map = gVoiceClient->getParticipantList();
+	if (voice_map)
+	{
+		for (LLVoiceClient::participantMap::const_iterator iter = voice_map->begin();
+			iter != voice_map->end(); ++iter)
+		{
+			LLUUID id = (*iter).second->mAvatarID;
+			speakers_uuids.push_back(id);
+		}
+	}
+}
+
+void LLCallFloater::initParticipantsVoiceState()
+{
+	// Set initial status for each participant in the list.
+	std::vector<LLPanel*> items;
+	mAvatarList->getItems(items);
+	std::vector<LLPanel*>::const_iterator
+		it = items.begin(),
+		it_end = items.end();
+
+
+	std::vector<LLUUID> speakers_uuids;
+	get_voice_participants_uuids(speakers_uuids);
+
+	for(; it != it_end; ++it)
+	{
+		LLAvatarListItem *item = dynamic_cast<LLAvatarListItem*>(*it);
+		
+		if (!item)	continue;
+		
+		LLUUID speaker_id = item->getAvatarId();
+
+		std::vector<LLUUID>::const_iterator speaker_iter = std::find(speakers_uuids.begin(), speakers_uuids.end(), speaker_id);
+
+		// If an avatarID assigned to a panel is found in a speakers list
+		// obtained from VoiceClient we assign the JOINED status to the owner
+		// of this avatarID.
+		if (speaker_iter != speakers_uuids.end())
+		{
+			setState(item, STATE_JOINED);
+		}
+		else
+		{
+			LLPointer<LLSpeaker> speakerp = mSpeakerManager->findSpeaker(speaker_id);
+			// If someone has already left the call before, we create his
+			// avatar row panel with HAS_LEFT status and remove it after
+			// the timeout, otherwise we create a panel with INVITED status
+			if (speakerp.notNull() && speakerp.get()->mHasLeftCurrentCall)
+			{
+				setState(item, STATE_LEFT);
+			}
+			else
+			{
+				setState(item, STATE_INVITED);
+			}
+		}
+	}
+}
+
+void LLCallFloater::updateParticipantsVoiceState()
+{
+	std::vector<LLUUID> speakers_list;
+
+	// Get a list of participants from VoiceClient
+	LLVoiceClient::participantMap *map = gVoiceClient->getParticipantList();
+	if (!map) return;
+
+	for (LLVoiceClient::participantMap::const_iterator iter = map->begin();
+		iter != map->end(); ++iter)
+	{
+		LLUUID id = (*iter).second->mAvatarID;
+//		if ( id != gAgent.getID() )
+		{
+			speakers_list.push_back(id);
+/*
+			LLAvatarListItem* item = dynamic_cast<LLAvatarListItem*> (mAvatarList->getItemByValue(id));
+			if (item)
+			{
+				setState(item, STATE_JOINED);
+			}
+*/
+
+		}
+	}
+
+	// Updating the status for each participant.
+	std::vector<LLPanel*> items;
+	mAvatarList->getItems(items);
+	std::vector<LLPanel*>::const_iterator
+		it = items.begin(),
+		it_end = items.end();
+
+	for(; it != it_end; ++it)
+	{
+		LLAvatarListItem *item = dynamic_cast<LLAvatarListItem*>(*it);
+		if (!item) continue;
+
+		const LLUUID participant_id = item->getAvatarId();
+		bool found = false;
+
+		std::vector<LLUUID>::iterator speakers_iter = std::find(speakers_list.begin(), speakers_list.end(), participant_id);
+
+		lldebugs << "processing speaker: " << item->getAvatarName() << ", " << item->getAvatarId() << llendl;
+
+		// If an avatarID assigned to a panel is found in a speakers list
+		// obtained from VoiceClient we assign the JOINED status to the owner
+		// of this avatarID.
+		if (speakers_iter != speakers_list.end())
+		{
+			setState(item, STATE_JOINED);
+
+			LLPointer<LLSpeaker> speaker = mSpeakerManager->findSpeaker(participant_id);
+			if (speaker.isNull())
+				continue;
+			speaker->mHasLeftCurrentCall = FALSE;
+
+			speakers_list.erase(speakers_iter);
+			found = true;
+		}
+
+		// If an avatarID is not found in a speakers list from VoiceClient and
+		// a panel with this ID has a JOINED status this means that this person
+		// HAS LEFT the call.
+		if (!found)
+		{
+			if ((getState(participant_id) == STATE_JOINED))
+			{
+				setState(item, STATE_LEFT);
+
+				LLPointer<LLSpeaker> speaker = mSpeakerManager->findSpeaker(item->getAvatarId());
+				if (speaker.isNull())
+					continue;
+
+				speaker->mHasLeftCurrentCall = TRUE;
+			}
+			else if ((getState(participant_id) != STATE_LEFT))
+			{
+				setState(item, STATE_INVITED);
+			}
+
+/*
+			// If there is already a started timer for the current panel don't do anything.
+			bool no_timer_for_current_panel = true;
+			if (mTimersMap.size() > 0)
+			{
+				timers_map::iterator found_it = mTimersMap.find(participant_id);
+				if (found_it != mTimersMap.end())
+				{
+					no_timer_for_current_panel = false;
+				}
+			}
+
+			if (no_timer_for_current_panel)
+			{
+				// Starting a timer to remove an avatar row panel after timeout
+				// *TODO Make the timeout period adjustable
+				mTimersMap.insert(timer_pair(participant_id, new LLAvatarRowRemoveTimer(this->getHandle(), 10, participant_id)));
+			}
+*/
+		}
+	}
+
+}
+
+void LLCallFloater::setState(LLAvatarListItem* item, ESpeakerState state)
+{
+	setState(item->getAvatarId(), state);
+
+	LLStyle::Params speaker_style;
+	LLFontDescriptor new_desc(speaker_style.font()->getFontDesc());
+
+	switch (state)
+	{
+	case STATE_INVITED:
+//		status_str = "INVITED";			// *TODO: localize
+		new_desc.setStyle(LLFontGL::NORMAL);
+		break;
+	case STATE_JOINED:
+//		status_str = "JOINED";			// *TODO: localize
+		new_desc.setStyle(LLFontGL::NORMAL);
+		break;
+	case STATE_LEFT:
+		{
+			//		status_str = "HAS LEFT CALL";	// *TODO: localize
+			new_desc.setStyle(LLFontGL::ITALIC);
+
+		}
+		break;
+	default:
+		llwarns << "Unrecognized avatar panel state (" << state << ")" << llendl;
+		break;
+	}
+
+	LLFontGL* new_font = LLFontGL::getFont(new_desc);
+	speaker_style.font = new_font;
+	item->setStyle(speaker_style);
+
+//	if ()
+	{
+		// found speaker is in voice, mark him as online
+		item->setOnline(STATE_JOINED == state);
+	}
+}
+
 //EOF
diff --git a/indra/newview/llcallfloater.h b/indra/newview/llcallfloater.h
index ac45e283eb9..21fba433c63 100644
--- a/indra/newview/llcallfloater.h
+++ b/indra/newview/llcallfloater.h
@@ -38,6 +38,7 @@
 #include "llvoiceclient.h"
 
 class LLAvatarList;
+class LLAvatarListItem;
 class LLNonAvatarCaller;
 class LLOutputMonitorCtrl;
 class LLParticipantList;
@@ -81,6 +82,16 @@ class LLCallFloater : public LLDockableFloater, LLVoiceClientParticipantObserver
 		VC_PEER_TO_PEER
 	}EVoiceControls;
 
+	typedef enum e_speaker_state
+	{
+		STATE_UNKNOWN,
+		STATE_INVITED,
+		STATE_JOINED,
+		STATE_LEFT,
+	} ESpeakerState;
+
+	typedef std::map<LLUUID, ESpeakerState> speaker_state_map_t;
+
 	void leaveCall();
 
 	/**
@@ -95,7 +106,7 @@ class LLCallFloater : public LLDockableFloater, LLVoiceClientParticipantObserver
 	 * Refreshes participant list according to current Voice Channel
 	 */
 	void refreshPartisipantList();
-
+	void onAvatarListRefreshed();
 
 	
 	void updateTitle();
@@ -103,7 +114,24 @@ class LLCallFloater : public LLDockableFloater, LLVoiceClientParticipantObserver
 	void setModeratorMutedVoice(bool moderator_muted);
 	void updateAgentModeratorState();
 
+	void initParticipantsVoiceState();
+	void updateParticipantsVoiceState();
+
+	void setState(LLAvatarListItem* item, ESpeakerState state);
+	void setState(const LLUUID& speaker_id, ESpeakerState state)
+	{
+		lldebugs << "Storing state: " << speaker_id << ", " << state << llendl;
+		mSpeakerStateMap[speaker_id] = state;
+	}
+
+	ESpeakerState getState(const LLUUID& speaker_id)
+	{
+		lldebugs << "Getting state: " << speaker_id << ", " << mSpeakerStateMap[speaker_id] << llendl;
+
+		return mSpeakerStateMap[speaker_id];
+	}
 private:
+	speaker_state_map_t mSpeakerStateMap;
 	LLSpeakerMgr* mSpeakerManager;
 	LLParticipantList* mPaticipants;
 	LLAvatarList* mAvatarList;
@@ -112,6 +140,11 @@ class LLCallFloater : public LLDockableFloater, LLVoiceClientParticipantObserver
 	LLPanel* mAgentPanel;
 	LLOutputMonitorCtrl* mSpeakingIndicator;
 	bool mIsModeratorMutedVoice;
+
+	bool mInitParticipantsVoiceState;
+
+	boost::signals2::connection mAvatarListRefreshConnection;
+
 };
 
 
diff --git a/indra/newview/llparticipantlist.cpp b/indra/newview/llparticipantlist.cpp
index dfe0b504bd4..5941487c7d5 100644
--- a/indra/newview/llparticipantlist.cpp
+++ b/indra/newview/llparticipantlist.cpp
@@ -208,27 +208,6 @@ LLParticipantList::EParticipantSortOrder LLParticipantList::getSortOrder()
 	return mSortOrder;
 }
 
-void LLParticipantList::refreshVoiceState()
-{
-	LLSpeakerMgr::speaker_list_t speakers;
-	mSpeakerMgr->getSpeakerList(&speakers, TRUE);
-
-	for (LLSpeakerMgr::speaker_list_t::iterator iter = speakers.begin();
-		iter != speakers.end(); ++iter)
-	{
-		LLSpeaker* speakerp = (*iter).get();
-		const LLUUID& speaker_id = speakerp->mID;
-		LLAvatarListItem* item = dynamic_cast<LLAvatarListItem*> (mAvatarList->getItemByValue(speaker_id));
-		if ( item )
-		{
-			// if voice is disabled for this speaker show non voice speakers as disabled
-			bool is_in_voice = speakerp->mStatus > LLSpeaker::STATUS_VOICE_ACTIVE
-				&& speakerp->mStatus != LLSpeaker::STATUS_MUTED;
-			item->setOnline(!is_in_voice);
-		}
-	}
-}
-
 void LLParticipantList::updateRecentSpeakersOrder()
 {
 	if (E_SORT_BY_RECENT_SPEAKERS == getSortOrder())
diff --git a/indra/newview/llparticipantlist.h b/indra/newview/llparticipantlist.h
index 515529452bd..c4eb1809176 100644
--- a/indra/newview/llparticipantlist.h
+++ b/indra/newview/llparticipantlist.h
@@ -59,12 +59,6 @@ class LLParticipantList
 		void setSortOrder(EParticipantSortOrder order = E_SORT_BY_NAME);
 		EParticipantSortOrder getSortOrder();
 
-		/**
-		 * Refreshes participants to display ones not in voice as disabled.
-		 * TODO: mantipov: probably should be moved into derived class for LLFloaterCall
-		 */
-		void refreshVoiceState();
-
 		/**
 		 * Refreshes the participant list if it's in sort by recent speaker order.
 		 */
-- 
GitLab