summaryrefslogtreecommitdiff
path: root/indra/newview/llcallfloater.cpp
diff options
context:
space:
mode:
Diffstat (limited to 'indra/newview/llcallfloater.cpp')
-rw-r--r--indra/newview/llcallfloater.cpp333
1 files changed, 254 insertions, 79 deletions
diff --git a/indra/newview/llcallfloater.cpp b/indra/newview/llcallfloater.cpp
index c222ced98f..6317a6a392 100644
--- a/indra/newview/llcallfloater.cpp
+++ b/indra/newview/llcallfloater.cpp
@@ -40,14 +40,20 @@
#include "llagent.h"
#include "llagentdata.h" // for gAgentID
+#include "llavatariconctrl.h"
#include "llavatarlist.h"
#include "llbottomtray.h"
#include "llimfloater.h"
#include "llfloaterreg.h"
#include "llparticipantlist.h"
#include "llspeakers.h"
+#include "lltextutil.h"
#include "lltransientfloatermgr.h"
+#include "llviewerwindow.h"
+#include "llvoicechannel.h"
+#include "lllayoutstack.h"
+static void get_voice_participants_uuids(std::vector<LLUUID>& speakers_uuids);
class LLNonAvatarCaller : public LLAvatarListItem
{
@@ -66,10 +72,18 @@ public:
showLastInteractionTime(false);
setShowProfileBtn(false);
setShowInfoBtn(false);
+ mAvatarIcon->setValue("Avaline_Icon");
+ mAvatarIcon->setToolTip(std::string(""));
}
return rv;
}
+ void setName(const std::string& name)
+ {
+ const std::string& formatted_phone = LLTextUtil::formatPhoneNumber(name);
+ LLAvatarListItem::setName(formatted_phone);
+ }
+
void setSpeakerId(const LLUUID& id) { mSpeakingIndicator->setSpeakerId(id); }
};
@@ -95,16 +109,12 @@ BOOL LLCallFloater::LLAvatarListItemRemoveTimer::tick()
return TRUE;
}
-
-LLCallFloater::Params::Params()
-: voice_left_remove_delay("voice_left_remove_delay", 10)
-{
-}
+LLVoiceChannel* LLCallFloater::sCurrentVoiceCanel = NULL;
LLCallFloater::LLCallFloater(const LLSD& key)
-: LLDockableFloater(NULL, false, key)
+: LLTransientDockableFloater(NULL, false, key)
, mSpeakerManager(NULL)
-, mPaticipants(NULL)
+, mParticipants(NULL)
, mAvatarList(NULL)
, mNonAvatarCaller(NULL)
, mVoiceType(VC_LOCAL_CHAT)
@@ -112,8 +122,11 @@ LLCallFloater::LLCallFloater(const LLSD& key)
, mSpeakingIndicator(NULL)
, mIsModeratorMutedVoice(false)
, mInitParticipantsVoiceState(false)
-, mVoiceLeftRemoveDelay(10) // TODO: mantipov: make xml driven
+, mVoiceLeftRemoveDelay(10)
{
+ static LLUICachedControl<S32> voice_left_remove_delay ("VoiceParticipantLeftRemoveDelay", 10);
+ mVoiceLeftRemoveDelay = voice_left_remove_delay;
+
mFactoryMap["non_avatar_caller"] = LLCallbackMap(create_non_avatar_caller, NULL);
LLVoiceClient::getInstance()->addObserver(this);
LLTransientFloaterMgr::getInstance()->addControlView(this);
@@ -123,10 +136,11 @@ LLCallFloater::~LLCallFloater()
{
resetVoiceRemoveTimers();
- delete mPaticipants;
- mPaticipants = NULL;
+ delete mParticipants;
+ mParticipants = NULL;
mAvatarListRefreshConnection.disconnect();
+ mVoiceChannelStateChangeConnection.disconnect();
// Don't use LLVoiceClient::getInstance() here
// singleton MAY have already been destroyed.
@@ -140,15 +154,16 @@ LLCallFloater::~LLCallFloater()
// virtual
BOOL LLCallFloater::postBuild()
{
- LLDockableFloater::postBuild();
+ LLTransientDockableFloater::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");
+ mNonAvatarCaller->setVisible(FALSE);
- LLView *anchor_panel = LLBottomTray::getInstance()->getChild<LLView>("speak_panel");
+ LLView *anchor_panel = LLBottomTray::getInstance()->getChild<LLView>("speak_flyout_btn");
setDockControl(new LLDockControl(
anchor_panel, this,
@@ -156,8 +171,8 @@ BOOL LLCallFloater::postBuild()
initAgentData();
- // update list for current session
- updateSession();
+
+ connectToChannel(LLVoiceChannel::getCurrentVoiceChannel());
return TRUE;
}
@@ -188,20 +203,37 @@ void LLCallFloater::draw()
}
// Need to resort the participant list if it's in sort by recent speaker order.
- if (mPaticipants)
- mPaticipants->updateRecentSpeakersOrder();
+ if (mParticipants)
+ mParticipants->updateRecentSpeakersOrder();
- LLDockableFloater::draw();
+ LLTransientDockableFloater::draw();
}
// virtual
void LLCallFloater::onChange()
{
- if (NULL == mPaticipants) return;
+ if (NULL == mParticipants) return;
updateParticipantsVoiceState();
+
+ // Add newly joined participants.
+ std::vector<LLUUID> speakers_uuids;
+ get_voice_participants_uuids(speakers_uuids);
+ for (std::vector<LLUUID>::const_iterator it = speakers_uuids.begin(); it != speakers_uuids.end(); it++)
+ {
+ mParticipants->addAvatarIDExceptAgent(*it);
+ }
}
+S32 LLCallFloater::notifyParent(const LLSD& info)
+{
+ if("size_changes" == info["action"])
+ {
+ reshapeToFitContent();
+ return 1;
+ }
+ return LLDockableFloater::notifyParent(info);
+}
//////////////////////////////////////////////////////////////////////////
/// PRIVATE SECTION
@@ -246,6 +278,11 @@ void LLCallFloater::updateSession()
case IM_NOTHING_SPECIAL:
case IM_SESSION_P2P_INVITE:
mVoiceType = VC_PEER_TO_PEER;
+
+ if (!im_session->mOtherParticipantIsAvatar)
+ {
+ mVoiceType = VC_PEER_TO_PEER_AVALINE;
+ }
break;
case IM_SESSION_CONFERENCE_START:
case IM_SESSION_GROUP_START:
@@ -278,9 +315,9 @@ void LLCallFloater::updateSession()
//hide "Leave Call" button for nearby chat
bool is_local_chat = mVoiceType == VC_LOCAL_CHAT;
- childSetVisible("leave_call_btn", !is_local_chat);
+ childSetVisible("leave_call_btn_panel", !is_local_chat);
- refreshPartisipantList();
+ refreshParticipantList();
updateAgentModeratorState();
//show floater for voice calls
@@ -291,42 +328,19 @@ void LLCallFloater::updateSession()
if (show_me)
{
setVisible(true);
- // Workaround(EM): Set current call dialog to front most because
- // connect/leaving popups should appear on top of VCP.
- // See bug EXT-3628.
- LLOutgoingCallDialog* instance =
- LLFloaterReg::findTypedInstance<LLOutgoingCallDialog>("outgoing_call", LLOutgoingCallDialog::OCD_KEY);
- if(instance && instance->getVisible())
- {
- instance->setFrontmost();
- }
}
}
}
-void LLCallFloater::refreshPartisipantList()
+void LLCallFloater::refreshParticipantList()
{
- // lets forget states from the previous session
- // for timers...
- resetVoiceRemoveTimers();
+ bool non_avatar_caller = VC_PEER_TO_PEER_AVALINE == mVoiceType;
- // ...and for speaker state
- mSpeakerStateMap.clear();
-
- delete mPaticipants;
- mPaticipants = NULL;
- mAvatarList->clear();
-
- bool non_avatar_caller = false;
- if (VC_PEER_TO_PEER == mVoiceType)
+ if (non_avatar_caller)
{
LLIMModel::LLIMSession* session = LLIMModel::instance().findIMSession(mSpeakerManager->getSessionID());
- non_avatar_caller = !session->mOtherParticipantIsAvatar;
- if (non_avatar_caller)
- {
- mNonAvatarCaller->setSpeakerId(session->mOtherParticipantID);
- mNonAvatarCaller->setName(session->mName);
- }
+ mNonAvatarCaller->setSpeakerId(session->mOtherParticipantID);
+ mNonAvatarCaller->setName(session->mName);
}
mNonAvatarCaller->setVisible(non_avatar_caller);
@@ -334,7 +348,8 @@ void LLCallFloater::refreshPartisipantList()
if (!non_avatar_caller)
{
- mPaticipants = new LLParticipantList(mSpeakerManager, mAvatarList, true, mVoiceType != VC_GROUP_CHAT && mVoiceType != VC_AD_HOC_CHAT);
+ mParticipants = new LLParticipantList(mSpeakerManager, mAvatarList, true, mVoiceType != VC_GROUP_CHAT && mVoiceType != VC_AD_HOC_CHAT);
+ mParticipants->setValidateSpeakerCallback(boost::bind(&LLCallFloater::validateSpeaker, this, _1));
if (LLLocalSpeakerMgr::getInstance() == mSpeakerManager)
{
@@ -360,21 +375,19 @@ void LLCallFloater::onAvatarListRefreshed()
}
}
+// static
void LLCallFloater::sOnCurrentChannelChanged(const LLUUID& /*session_id*/)
{
- // Don't update participant list if no channel info is available.
- // Fix for ticket EXT-3427
- // @see LLParticipantList::~LLParticipantList()
- if(LLVoiceChannel::getCurrentVoiceChannel() &&
- LLVoiceChannel::STATE_NO_CHANNEL_INFO == LLVoiceChannel::getCurrentVoiceChannel()->getState())
- {
- return;
- }
+ LLVoiceChannel* channel = LLVoiceChannel::getCurrentVoiceChannel();
+
+ // *NOTE: if signal was sent for voice channel with LLVoiceChannel::STATE_NO_CHANNEL_INFO
+ // it sill be sent for the same channel again (when state is changed).
+ // So, lets ignore this call.
+ if (channel == sCurrentVoiceCanel) return;
+
LLCallFloater* call_floater = LLFloaterReg::getTypedInstance<LLCallFloater>("voice_controls");
- // Forget speaker manager from the previous session to avoid using it after session was destroyed.
- call_floater->mSpeakerManager = NULL;
- call_floater->updateSession();
+ call_floater->connectToChannel(channel);
}
void LLCallFloater::updateTitle()
@@ -387,9 +400,17 @@ void LLCallFloater::updateTitle()
title = getString("title_nearby");
break;
case VC_PEER_TO_PEER:
+ case VC_PEER_TO_PEER_AVALINE:
{
+ title = voice_channel->getSessionName();
+
+ if (VC_PEER_TO_PEER_AVALINE == mVoiceType)
+ {
+ title = LLTextUtil::formatPhoneNumber(title);
+ }
+
LLStringUtil::format_map_t args;
- args["[NAME]"] = voice_channel->getSessionName();
+ args["[NAME]"] = title;
title = getString("title_peer_2_peer", args);
}
break;
@@ -459,7 +480,7 @@ void LLCallFloater::updateAgentModeratorState()
mAgentPanel->childSetValue("user_text", name);
}
-void get_voice_participants_uuids(std::vector<LLUUID>& speakers_uuids)
+static void get_voice_participants_uuids(std::vector<LLUUID>& speakers_uuids)
{
// Get a list of participants from VoiceClient
LLVoiceClient::participantMap *voice_map = gVoiceClient->getParticipantList();
@@ -576,7 +597,9 @@ void LLCallFloater::updateParticipantsVoiceState()
LLPointer<LLSpeaker> speaker = mSpeakerManager->findSpeaker(item->getAvatarId());
if (speaker.isNull())
+ {
continue;
+ }
speaker->mHasLeftCurrentCall = TRUE;
}
@@ -587,9 +610,12 @@ void LLCallFloater::updateParticipantsVoiceState()
{
setState(item, STATE_INVITED);
}
+ else
+ {
+ llwarns << "Unsupported (" << getState(participant_id) << ") state: " << item->getAvatarName() << llendl;
+ }
}
}
-
}
void LLCallFloater::setState(LLAvatarListItem* item, ESpeakerState state)
@@ -609,38 +635,25 @@ 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:
- new_desc.setStyle(LLFontGL::NORMAL);
+ item->setState(LLAvatarListItem::IS_VOICE_INVITED);
break;
case STATE_JOINED:
removeVoiceRemoveTimer(item->getAvatarId());
- new_desc.setStyle(LLFontGL::NORMAL);
+ item->setState(LLAvatarListItem::IS_VOICE_JOINED);
break;
case STATE_LEFT:
{
setVoiceRemoveTimer(item->getAvatarId());
- new_desc.setStyle(LLFontGL::ITALIC);
+ item->setState(LLAvatarListItem::IS_VOICE_LEFT);
}
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);
- }
}
void LLCallFloater::setVoiceRemoveTimer(const LLUUID& voice_speaker_id)
@@ -709,4 +722,166 @@ void LLCallFloater::removeVoiceRemoveTimer(const LLUUID& voice_speaker_id)
}
}
+bool LLCallFloater::validateSpeaker(const LLUUID& speaker_id)
+{
+ bool is_valid = true;
+ switch (mVoiceType)
+ {
+ case VC_LOCAL_CHAT:
+ {
+ // A nearby chat speaker is considered valid it it's known to LLVoiceClient (i.e. has enabled voice).
+ std::vector<LLUUID> speakers;
+ get_voice_participants_uuids(speakers);
+ is_valid = std::find(speakers.begin(), speakers.end(), speaker_id) != speakers.end();
+ }
+ break;
+ case VC_GROUP_CHAT:
+ // if participant had left this call before do not allow add her again. See EXT-4216.
+ // but if she Join she will be added into the list from the LLCallFloater::onChange()
+ is_valid = STATE_LEFT != getState(speaker_id);
+ break;
+ default:
+ // do nothing. required for Linux build
+ break;
+ }
+
+ return is_valid;
+}
+
+void LLCallFloater::connectToChannel(LLVoiceChannel* channel)
+{
+ mVoiceChannelStateChangeConnection.disconnect();
+
+ sCurrentVoiceCanel = channel;
+
+ mVoiceChannelStateChangeConnection = sCurrentVoiceCanel->setStateChangedCallback(boost::bind(&LLCallFloater::onVoiceChannelStateChanged, this, _1, _2));
+
+ updateState(channel->getState());
+}
+
+void LLCallFloater::onVoiceChannelStateChanged(const LLVoiceChannel::EState& old_state, const LLVoiceChannel::EState& new_state)
+{
+ updateState(new_state);
+}
+
+void LLCallFloater::updateState(const LLVoiceChannel::EState& new_state)
+{
+ LL_DEBUGS("Voice") << "Updating state: " << new_state << ", session name: " << sCurrentVoiceCanel->getSessionName() << LL_ENDL;
+ if (LLVoiceChannel::STATE_CONNECTED == new_state)
+ {
+ updateSession();
+ }
+ else
+ {
+ reset();
+ }
+}
+
+void LLCallFloater::reset()
+{
+ // lets forget states from the previous session
+ // for timers...
+ resetVoiceRemoveTimers();
+
+ // ...and for speaker state
+ mSpeakerStateMap.clear();
+
+ delete mParticipants;
+ mParticipants = NULL;
+ mAvatarList->clear();
+
+ // update floater to show Loading while waiting for data.
+ mAvatarList->setNoItemsCommentText(LLTrans::getString("LoadingData"));
+ mAvatarList->setVisible(TRUE);
+ mNonAvatarCaller->setVisible(FALSE);
+
+ mSpeakerManager = NULL;
+}
+
+void reshape_floater(LLCallFloater* floater, S32 delta_height)
+{
+ // Try to update floater top side if it is docked(to bottom bar).
+ // Try to update floater bottom side or top side if it is un-docked.
+ // If world rect is too small, floater will not be reshaped at all.
+
+ LLRect floater_rect = floater->getRect();
+ LLRect world_rect = gViewerWindow->getWorldViewRectScaled();
+
+ // floater is docked to bottom bar
+ if(floater->isDocked())
+ {
+ // can update floater top side
+ if(floater_rect.mTop + delta_height < world_rect.mTop)
+ {
+ floater_rect.set(floater_rect.mLeft, floater_rect.mTop + delta_height,
+ floater_rect.mRight, floater_rect.mBottom);
+ }
+ }
+ // floater is un-docked
+ else
+ {
+ // can update floater bottom side
+ if( floater_rect.mBottom - delta_height >= world_rect.mBottom )
+ {
+ floater_rect.set(floater_rect.mLeft, floater_rect.mTop,
+ floater_rect.mRight, floater_rect.mBottom - delta_height);
+ }
+ // could not update floater bottom side, check if we can update floater top side
+ else if( floater_rect.mTop + delta_height < world_rect.mTop )
+ {
+ floater_rect.set(floater_rect.mLeft, floater_rect.mTop + delta_height,
+ floater_rect.mRight, floater_rect.mBottom);
+ }
+ }
+
+ floater->setShape(floater_rect);
+ floater->getChild<LLLayoutStack>("my_call_stack")->updateLayout(FALSE);
+}
+
+void LLCallFloater::reshapeToFitContent()
+{
+ const S32 ITEM_HEIGHT = getParticipantItemHeight();
+ static const S32 MAX_VISIBLE_ITEMS = getMaxVisibleItems();
+
+ static S32 items_pad = mAvatarList->getItemsPad();
+ S32 list_height = mAvatarList->getRect().getHeight();
+ S32 items_height = mAvatarList->getItemsRect().getHeight();
+ if(items_height <= 0)
+ {
+ // make "no one near" text visible
+ items_height = ITEM_HEIGHT + items_pad;
+ }
+ S32 max_list_height = MAX_VISIBLE_ITEMS * ITEM_HEIGHT + items_pad * (MAX_VISIBLE_ITEMS - 1);
+ max_list_height += 2* mAvatarList->getBorderWidth();
+
+ S32 delta = items_height - list_height;
+ // too many items, don't reshape floater anymore, let scroll bar appear.
+ if(items_height > max_list_height)
+ {
+ delta = max_list_height - list_height;
+ }
+
+ reshape_floater(this, delta);
+}
+
+S32 LLCallFloater::getParticipantItemHeight()
+{
+ std::vector<LLPanel*> items;
+ mAvatarList->getItems(items);
+ if(items.size() > 0)
+ {
+ return items[0]->getRect().getHeight();
+ }
+ else
+ {
+ return getChild<LLPanel>("non_avatar_caller")->getRect().getHeight();
+ }
+}
+
+S32 LLCallFloater::getMaxVisibleItems()
+{
+ static LLCachedControl<S32> max_visible_items(*LLUI::sSettingGroups["config"],"CallFloaterMaxItems");
+ return max_visible_items;
+}
+
//EOF