diff --git a/indra/llui/llscrolllistctrl.cpp b/indra/llui/llscrolllistctrl.cpp index 788adfa70..15d1345ef 100644 --- a/indra/llui/llscrolllistctrl.cpp +++ b/indra/llui/llscrolllistctrl.cpp @@ -502,12 +502,14 @@ BOOL LLScrollListCtrl::addItem( LLScrollListItem* item, EAddPosition pos, BOOL r case ADD_SORTED: { - // sort by column 0, in ascending order - std::vector single_sort_column; - single_sort_column.push_back(std::make_pair(0, TRUE)); - mItemList.push_back(item); - std::stable_sort(mItemList.begin(), mItemList.end(), SortScrollListItem(single_sort_column,mSortCallback)); + // std::stable_sort is expensive. Only do this if the user sort criteria is not column 0, otherwise + // setNeedsSort does what we want. + if (mSortColumns.empty() || mSortColumns[0].first != 0) + { + // sort by column 0, in ascending order + std::stable_sort(mItemList.begin(), mItemList.end(), SortScrollListItem({ {0,true} }, mSortCallback)); + } // ADD_SORTED just sorts by first column... // this might not match user sort criteria, so flag list as being in unsorted state diff --git a/indra/llui/llscrolllistctrl.h b/indra/llui/llscrolllistctrl.h index e10a29373..b4dca4fc0 100644 --- a/indra/llui/llscrolllistctrl.h +++ b/indra/llui/llscrolllistctrl.h @@ -369,6 +369,7 @@ public: return mSortCallback->connect(cb); } + S32 getLinesPerPage(); protected: // "Full" interface: use this when you're creating a list that has one or more of the following: @@ -405,7 +406,6 @@ private: void deselectItem(LLScrollListItem* itemp); void commitIfChanged(); BOOL setSort(S32 column, BOOL ascending); - S32 getLinesPerPage(); S32 mLineHeight; // the max height of a single line S32 mScrollLines; // how many lines we've scrolled down diff --git a/indra/newview/llfloaterchat.cpp b/indra/newview/llfloaterchat.cpp index 449fe6028..95c683582 100644 --- a/indra/newview/llfloaterchat.cpp +++ b/indra/newview/llfloaterchat.cpp @@ -312,7 +312,7 @@ void LLFloaterChat::addChatHistory(const LLChat& chat, bool log_to_file) // add objects as transient speakers that can be muted if (chat.mSourceType == CHAT_SOURCE_OBJECT) { - LLLocalSpeakerMgr::getInstance()->setSpeaker(chat.mFromID, chat.mFromName, LLSpeaker::STATUS_NOT_IN_CHANNEL, LLSpeaker::SPEAKER_OBJECT); + LLLocalSpeakerMgr::getInstance()->setSpeaker({ chat.mFromID, LLSpeaker::SPEAKER_OBJECT, LLSpeaker::STATUS_NOT_IN_CHANNEL, boost::none, boost::none, chat.mFromName }); } // start tab flashing on incoming text from other users (ignoring system text, etc) diff --git a/indra/newview/llparticipantlist.cpp b/indra/newview/llparticipantlist.cpp index 499c014e3..9b3887395 100644 --- a/indra/newview/llparticipantlist.cpp +++ b/indra/newview/llparticipantlist.cpp @@ -60,11 +60,13 @@ LLParticipantList::LLParticipantList(LLSpeakerMgr* data_source, mSpeakerMuteListener = new SpeakerMuteListener(*this); mSpeakerBatchBeginListener = new SpeakerBatchBeginListener(*this); mSpeakerBatchEndListener = new SpeakerBatchEndListener(*this); + mSpeakerSortingUpdateListener = new SpeakerSortingUpdateListener(*this); mSpeakerMgr->addListener(mSpeakerAddListener, "add"); mSpeakerMgr->addListener(mSpeakerRemoveListener, "remove"); mSpeakerMgr->addListener(mSpeakerClearListener, "clear"); mSpeakerMgr->addListener(mSpeakerBatchBeginListener, "batch_begin"); mSpeakerMgr->addListener(mSpeakerBatchEndListener, "batch_end"); + mSpeakerMgr->addListener(mSpeakerSortingUpdateListener, "update_sorting"); //mSpeakerMgr->addListener(mSpeakerModeratorListener, "update_moderator"); } @@ -229,6 +231,11 @@ void LLParticipantList::handleSpeakerSelect() void LLParticipantList::refreshSpeakers() { + if (mUpdateTimer.getElapsedTimeF32() < .5f) + { + return; + } + mUpdateTimer.reset(); // store off current selection and scroll state to preserve across list rebuilds const S32 scroll_pos = mAvatarList->getScrollInterface()->getScrollPos(); @@ -239,27 +246,42 @@ void LLParticipantList::refreshSpeakers() // panel and hasn't been motionless for more than a few seconds. see DEV-6655 -MG LLRect screen_rect; localRectToScreen(getLocalRect(), &screen_rect); - mSpeakerMgr->update(!(screen_rect.pointInRect(gViewerWindow->getCurrentMouseX(), gViewerWindow->getCurrentMouseY()) && gMouseIdleTimer.getElapsedTimeF32() < 5.f)); + bool resort_ok = !(screen_rect.pointInRect(gViewerWindow->getCurrentMouseX(), gViewerWindow->getCurrentMouseY()) && gMouseIdleTimer.getElapsedTimeF32() < 5.f); + mSpeakerMgr->update(resort_ok); + bool re_sort = false; + int start_pos = llmax(0, scroll_pos - 20); + int end_pos = scroll_pos + mAvatarList->getLinesPerPage() + 20; std::vector items = mAvatarList->getAllData(); + if (start_pos >= items.size()) + { + return; + } + int count = 0; for (std::vector::iterator item_it = items.begin(); item_it != items.end(); ++item_it) { + LLScrollListItem* itemp = (*item_it); LLPointer speakerp = mSpeakerMgr->findSpeaker(itemp->getUUID()); if (speakerp.isNull()) continue; - if (LLScrollListCell* icon_cell = itemp->getColumn(0)) + ++count; + + // Color changes. Only perform for rows that are near or in the viewable area. + if (count > start_pos && count <= end_pos) { - if (speakerp->mStatus == LLSpeaker::STATUS_MUTED) + if (LLScrollListCell* icon_cell = itemp->getColumn(0)) { - icon_cell->setValue("mute_icon.tga"); - static const LLCachedControl sAscentMutedColor("AscentMutedColor"); - icon_cell->setColor(speakerp->mModeratorMutedVoice ? /*LLColor4::grey*/sAscentMutedColor : LLColor4(1.f, 71.f / 255.f, 71.f / 255.f, 1.f)); - } - else - { - switch(llmin(2, llfloor((speakerp->mSpeechVolume / LLVoiceClient::OVERDRIVEN_POWER_LEVEL) * 3.f))) + if (speakerp->mStatus == LLSpeaker::STATUS_MUTED) { + icon_cell->setValue("mute_icon.tga"); + static const LLCachedControl sAscentMutedColor("AscentMutedColor"); + icon_cell->setColor(speakerp->mModeratorMutedVoice ? /*LLColor4::grey*/sAscentMutedColor : LLColor4(1.f, 71.f / 255.f, 71.f / 255.f, 1.f)); + } + else + { + switch (llmin(2, llfloor((speakerp->mSpeechVolume / LLVoiceClient::OVERDRIVEN_POWER_LEVEL) * 3.f))) + { case 0: icon_cell->setValue("icn_active-speakers-dot-lvl0.tga"); break; @@ -269,62 +291,66 @@ void LLParticipantList::refreshSpeakers() case 2: icon_cell->setValue("icn_active-speakers-dot-lvl2.tga"); break; + } + // non voice speakers have hidden icons, render as transparent + icon_cell->setColor(speakerp->mStatus > LLSpeaker::STATUS_VOICE_ACTIVE ? LLColor4::transparent : speakerp->mDotColor); } - // non voice speakers have hidden icons, render as transparent - icon_cell->setColor(speakerp->mStatus > LLSpeaker::STATUS_VOICE_ACTIVE ? LLColor4::transparent : speakerp->mDotColor); } - } - // update name column - if (LLScrollListCell* name_cell = itemp->getColumn(1)) - { - if (speakerp->mStatus == LLSpeaker::STATUS_NOT_IN_CHANNEL) + // update name column + if (LLScrollListCell* name_cell = itemp->getColumn(1)) { - // draw inactive speakers in different color - static const LLCachedControl sSpeakersInactive(gColors, "SpeakersInactive"); - name_cell->setColor(sSpeakersInactive); - } - else - { - // - bool found = mShowTextChatters || speakerp->mID == gAgentID; - const LLWorld::region_list_t& regions = LLWorld::getInstance()->getRegionList(); - for (LLWorld::region_list_t::const_iterator iter = regions.begin(); !found && iter != regions.end(); ++iter) + if (speakerp->mStatus == LLSpeaker::STATUS_NOT_IN_CHANNEL) { - // Are they in this sim? - if (const LLViewerRegion* regionp = *iter) - if (std::find(regionp->mMapAvatarIDs.begin(), regionp->mMapAvatarIDs.end(), speakerp->mID) != regionp->mMapAvatarIDs.end()) - found = true; - } - if (!found) - { - static const LLCachedControl sSpeakersGhost(gColors, "SpeakersGhost"); - name_cell->setColor(sSpeakersGhost); + // draw inactive speakers in different color + static const LLCachedControl sSpeakersInactive(gColors, "SpeakersInactive"); + name_cell->setColor(sSpeakersInactive); } else - // { - static const LLCachedControl sDefaultListText(gColors, "DefaultListText"); - name_cell->setColor(sDefaultListText); + // + bool found = mShowTextChatters || speakerp->mID == gAgentID; + const LLWorld::region_list_t& regions = LLWorld::getInstance()->getRegionList(); + for (LLWorld::region_list_t::const_iterator iter = regions.begin(); !found && iter != regions.end(); ++iter) + { + // Are they in this sim? + if (const LLViewerRegion* regionp = *iter) + if (std::find(regionp->mMapAvatarIDs.begin(), regionp->mMapAvatarIDs.end(), speakerp->mID) != regionp->mMapAvatarIDs.end()) + found = true; + } + if (!found) + { + static const LLCachedControl sSpeakersGhost(gColors, "SpeakersGhost"); + name_cell->setColor(sSpeakersGhost); + } + else + // + { + static const LLCachedControl sDefaultListText(gColors, "DefaultListText"); + name_cell->setColor(sDefaultListText); + } } } - + } + // update name column. Need to update all rows to make name sorting behave correctly. + if (LLScrollListCell* name_cell = itemp->getColumn(1)) + { std::string speaker_name = speakerp->mDisplayName.empty() ? LLCacheName::getDefaultName() : speakerp->mDisplayName; if (speakerp->mIsModerator) speaker_name += " " + getString("moderator_label"); - name_cell->setValue(speaker_name); + if (name_cell->getValue().asString() != speaker_name) + { + re_sort = true; + name_cell->setValue(speaker_name); + } static_cast(name_cell)->setFontStyle(speakerp->mIsModerator ? LLFontGL::BOLD : LLFontGL::NORMAL); } - // update speaking order column - if (LLScrollListCell* speaking_status_cell = itemp->getColumn(2)) - { - // since we are forced to sort by text, encode sort order as string - // print speaking ordinal in a text-sorting friendly manner - speaking_status_cell->setValue(llformat("%010d", speakerp->mSortIndex)); - } } // we potentially modified the sort order by touching the list items - mAvatarList->setNeedsSort(); + if (re_sort) + { + mAvatarList->setNeedsSortColumn(1); + } // keep scroll value stable mAvatarList->getScrollInterface()->setScrollPos(scroll_pos); @@ -391,6 +417,34 @@ void LLParticipantList::onSpeakerBatchEndEvent() mAvatarList->setSortEnabled(true); } +void LLParticipantList::onSpeakerSortingUpdateEvent() +{ + bool re_sort = false; + for (auto&& item : mAvatarList->getAllData()) + { + // update speaking order column + if (LLScrollListCell* speaking_status_cell = item->getColumn(2)) + { + LLPointer speakerp = mSpeakerMgr->findSpeaker(item->getUUID()); + if (speakerp) + { + re_sort = true; + std::string sort_index = llformat("%010d", speakerp->mSortIndex); + // since we are forced to sort by text, encode sort order as string + // print speaking ordinal in a text-sorting friendly manner + if (speaking_status_cell->getValue().asString() != sort_index) + { + speaking_status_cell->setValue(sort_index); + } + } + } + } + if (re_sort) + { + mAvatarList->setNeedsSortColumn(2); + } +} + void LLParticipantList::addAvatarIDExceptAgent(const LLUUID& avatar_id) { //if (mExcludeAgent && gAgent.getID() == avatar_id) return; @@ -492,6 +546,12 @@ bool LLParticipantList::SpeakerBatchEndListener::handleEvent(LLPointer event, const LLSD& userdata) +{ + mParent.onSpeakerSortingUpdateEvent(); + return true; +} + // Singu Note: The following functions are actually of the LLParticipantListMenu class, but we haven't married lists with menus yet. void LLParticipantList::toggleAllowTextChat(const LLSD& userdata) { diff --git a/indra/newview/llparticipantlist.h b/indra/newview/llparticipantlist.h index 17be7bc78..0c9c292e5 100644 --- a/indra/newview/llparticipantlist.h +++ b/indra/newview/llparticipantlist.h @@ -78,6 +78,7 @@ protected: bool onSpeakerMuteEvent(LLPointer event, const LLSD& userdata); void onSpeakerBatchBeginEvent(); void onSpeakerBatchEndEvent(); + void onSpeakerSortingUpdateEvent(); /** * List of listeners implementing LLOldEvents::LLSimpleListener. @@ -142,6 +143,13 @@ protected: /*virtual*/ bool handleEvent(LLPointer event, const LLSD& userdata); }; + class SpeakerSortingUpdateListener : public BaseSpeakerListener + { + public: + SpeakerSortingUpdateListener(LLParticipantList& parent) : BaseSpeakerListener(parent) {} + /*virtual*/ bool handleEvent(LLPointer event, const LLSD& userdata); + }; + /** * Menu used in the participant list. class LLParticipantListMenu : public LLListContextMenu @@ -212,6 +220,7 @@ private: LLSpeakerMgr* mSpeakerMgr; LLScrollListCtrl* mAvatarList; bool mShowTextChatters; + LLFrameTimer mUpdateTimer; LLPointer mSpeakerAddListener; LLPointer mSpeakerRemoveListener; @@ -220,6 +229,7 @@ private: LLPointer mSpeakerMuteListener; LLPointer mSpeakerBatchBeginListener; LLPointer mSpeakerBatchEndListener; + LLPointer mSpeakerSortingUpdateListener; validate_speaker_callback_t mValidateSpeakerCallback; }; diff --git a/indra/newview/llspeakers.cpp b/indra/newview/llspeakers.cpp index dc352bc18..b0edaa9ab 100644 --- a/indra/newview/llspeakers.cpp +++ b/indra/newview/llspeakers.cpp @@ -45,31 +45,54 @@ 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), +LLSpeaker::LLSpeaker(const speaker_entry_t& entry) : + mID(entry.id), + mStatus(entry.status), + mType(entry.type), + mIsModerator(entry.moderator != boost::none && *entry.moderator), + mModeratorMutedText(entry.moderator_muted_text != boost::none && *entry.moderator_muted_text), + mModeratorMutedVoice(FALSE), 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) + mDisplayName(entry.name), + mNeedsResort(true) { - if (name.empty() && type == SPEAKER_AGENT) + if (mType == SPEAKER_AGENT) { lookupName(); } - else - { - mDisplayName = name; - } } +void LLSpeaker::update(const speaker_entry_t& entry) +{ + // keep highest priority status (lowest value) instead of overriding current value + setStatus(llmin(mStatus, entry.status)); + if (entry.moderator != boost::none) + { + mIsModerator = *entry.moderator; + } + if (entry.moderator_muted_text != boost::none) + { + mModeratorMutedText = *entry.moderator_muted_text; + }; + + // 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 (entry.type == LLSpeaker::SPEAKER_AGENT) + { + mType = LLSpeaker::SPEAKER_AGENT; + lookupName(); + } + + if (mDisplayName.empty()) + setName(entry.name); +} void LLSpeaker::lookupName() { @@ -77,7 +100,7 @@ void LLSpeaker::lookupName() { // [RLVa:KB] - Checked: 2009-07-10 (RLVa-1.0.0g) | Added: RLVa-1.0.0g if (gRlvHandler.hasBehaviour(RLV_BHVR_SHOWNAMES) && gAgentID != mID) - mDisplayName = RlvStrings::getAnonym(mDisplayName); + setName(RlvStrings::getAnonym(mDisplayName)); else // [/RLVa:KB] LLAvatarNameCache::get(mID, boost::bind(&LLSpeaker::onNameCache, this, _2)); @@ -88,9 +111,9 @@ void LLSpeaker::onNameCache(const LLAvatarName& full_name) { static const LLCachedControl name_system("SpeakerNameSystem"); if (!name_system) - mDisplayName = gCacheName->cleanFullName(full_name.getLegacyName()); + setName(gCacheName->cleanFullName(full_name.getLegacyName())); else - mDisplayName = full_name.getNSName(name_system); + setName(full_name.getNSName(name_system)); } bool LLSpeaker::isInVoiceChannel() @@ -200,7 +223,7 @@ BOOL LLSpeakerActionTimer::tick() void LLSpeakerActionTimer::unset() { - mActionCallback = NULL; + mActionCallback = nullptr; } LLSpeakersDelayActionsStorage::LLSpeakersDelayActionsStorage(LLSpeakerActionTimer::action_callback_t action_cb, F32 action_delay) @@ -338,22 +361,23 @@ LLSpeakerMgr::~LLSpeakerMgr() delete mSpeakerDelayRemover; } -void LLSpeakerMgr::setSpeakers(const std::vector& speakers) +void LLSpeakerMgr::setSpeakers(const std::vector& speakers) { if (!speakers.empty()) { fireEvent(new LLOldEvents::LLEvent(this), "batch_begin"); for (auto entry : speakers) { - setSpeaker(entry.id, entry.name, entry.status, entry.type, entry.moderator, entry.moderator_muted_text); + setSpeaker(entry); } fireEvent(new LLOldEvents::LLEvent(this), "batch_end"); } } -LLPointer LLSpeakerMgr::setSpeaker(const LLUUID& id, const std::string& name, LLSpeaker::ESpeakerStatus status, LLSpeaker::ESpeakerType type, bool moderator, bool moderator_muted_text) +LLPointer LLSpeakerMgr::setSpeaker(const speaker_entry_t& entry) { LLUUID session_id = getSessionID(); + const LLUUID& id = entry.id; if (id.isNull() || (id == session_id)) { return NULL; @@ -363,38 +387,16 @@ LLPointer LLSpeakerMgr::setSpeaker(const LLUUID& id, const std::strin auto it = mSpeakers.find(id); if (it == mSpeakers.end() || it->second.isNull()) { - speakerp = new LLSpeaker(id, name, type); - speakerp->mStatus = status; - speakerp->mIsModerator = moderator; - speakerp->mModeratorMutedText = moderator_muted_text; - mSpeakers.insert(std::make_pair(speakerp->mID, speakerp)); - mSpeakersSorted.push_back(speakerp); - fireEvent(new LLSpeakerListChangeEvent(this, speakerp->mID), "add"); + mSpeakersSorted.emplace_back(new LLSpeaker(entry)); + mSpeakers.emplace(id, mSpeakersSorted.back()); + fireEvent(new LLSpeakerListChangeEvent(this, id), "add"); } else { - speakerp = it->second; - bool update_moderator = (bool)speakerp->mIsModerator != moderator; - - // keep highest priority status (lowest value) instead of overriding current value - speakerp->mStatus = llmin(speakerp->mStatus, status); - speakerp->mIsModerator = moderator; - speakerp->mModeratorMutedText = moderator_muted_text; - // 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(); - } - if (update_moderator) - { - fireEvent(new LLSpeakerUpdateModeratorEvent(speakerp), "update_moderator"); - } + it->second->update(entry); } - mSpeakerDelayRemover->unsetActionTimer(speakerp->mID); + mSpeakerDelayRemover->unsetActionTimer(entry.id); return speakerp; } @@ -442,7 +444,8 @@ void LLSpeakerMgr::update(BOOL resort_ok) // 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++) + bool re_sort = false; + 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; @@ -450,7 +453,7 @@ void LLSpeakerMgr::update(BOOL resort_ok) 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); + bool moderator_muted_voice = LLVoiceClient::getInstance()->getIsModeratorMuted(speaker_id); if (moderator_muted_voice != speakerp->mModeratorMutedVoice) { speakerp->mModeratorMutedVoice = moderator_muted_voice; @@ -460,18 +463,18 @@ void LLSpeakerMgr::update(BOOL resort_ok) if (LLVoiceClient::getInstance()->getOnMuteList(speaker_id) || speakerp->mModeratorMutedVoice) { - speakerp->mStatus = LLSpeaker::STATUS_MUTED; + speakerp->setStatus(LLSpeaker::STATUS_MUTED); } else if (LLVoiceClient::getInstance()->getIsSpeaking(speaker_id)) { // reset inactivity expiration if (speakerp->mStatus != LLSpeaker::STATUS_SPEAKING) { - speakerp->mLastSpokeTime = mSpeechTimer.getElapsedTimeF32(); + speakerp->setSpokenTime(mSpeechTimer.getElapsedTimeF32()); speakerp->mHasSpoken = TRUE; fireEvent(new LLSpeakerUpdateSpeakerEvent(speakerp), "update_speaker"); } - speakerp->mStatus = LLSpeaker::STATUS_SPEAKING; + speakerp->setStatus(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) @@ -487,12 +490,12 @@ void LLSpeakerMgr::update(BOOL resort_ok) if (speakerp->mHasSpoken) { // have spoken once, not currently speaking - speakerp->mStatus = LLSpeaker::STATUS_HAS_SPOKEN; + speakerp->setStatus(LLSpeaker::STATUS_HAS_SPOKEN); } else { // default state for being in voice channel - speakerp->mStatus = LLSpeaker::STATUS_VOICE_ACTIVE; + speakerp->setStatus(LLSpeaker::STATUS_VOICE_ACTIVE); } } } @@ -506,39 +509,53 @@ void LLSpeakerMgr::update(BOOL resort_ok) } else { - speakerp->mStatus = LLSpeaker::STATUS_TEXT_ONLY; + speakerp->setStatus(LLSpeaker::STATUS_TEXT_ONLY); speakerp->mSpeechVolume = 0.f; speakerp->mDotColor = ACTIVE_COLOR; } } + if (speakerp->mNeedsResort) + { + re_sort = true; + speakerp->mNeedsResort = false; + } } - if(resort_ok) // only allow list changes when user is not interacting with it + if (resort_ok && re_sort) // 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 + // 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 speakerp = *sorted_speaker_it; - - // color code recent speakers who are not currently speaking - if (speakerp->mStatus == LLSpeaker::STATUS_HAS_SPOKEN) + bool index_changed = false; + S32 recent_speaker_count = 0; + S32 sort_index = 0; + for (speaker_list_t::iterator sorted_speaker_it = mSpeakersSorted.begin(); + sorted_speaker_it != mSpeakersSorted.end(); + ++sorted_speaker_it) { - speakerp->mDotColor = lerp(speaking_color, ACTIVE_COLOR, clamp_rescale((F32)recent_speaker_count, -2.f, 3.f, 0.f, 1.f)); - recent_speaker_count++; - } + LLPointer speakerp = *sorted_speaker_it; - // stuff sort ordinal into speaker so the ui can sort by this value - speakerp->mSortIndex = sort_index++; + // 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 + if (speakerp->mSortIndex != sort_index++) + { + speakerp->mSortIndex = sort_index-1; + index_changed = true; + } + } + if (index_changed) + { + fireEvent(new LLOldEvents::LLEvent(this), "update_sorting"); + } } } @@ -554,11 +571,13 @@ void LLSpeakerMgr::updateSpeakerList() speakers.reserve(participants.size()); for (std::set::iterator participant_it = participants.begin(); participant_it != participants.end(); ++participant_it) { - speakers.emplace_back(speaker_entry_t( + speakers.emplace_back( *participant_it, + (LLVoiceClient::getInstance()->isParticipantAvatar(*participant_it) ? LLSpeaker::SPEAKER_AGENT : LLSpeaker::SPEAKER_EXTERNAL), LLSpeaker::STATUS_VOICE_ACTIVE, - LLVoiceClient::getInstance()->getDisplayName(*participant_it), - (LLVoiceClient::getInstance()->isParticipantAvatar(*participant_it)?LLSpeaker::SPEAKER_AGENT:LLSpeaker::SPEAKER_EXTERNAL))); + boost::none, + boost::none, + LLVoiceClient::getInstance()->getDisplayName(*participant_it)); } setSpeakers(speakers); } @@ -602,12 +621,11 @@ void LLSpeakerMgr::updateSpeakerList() const std::string& localized_online(); if ((member->getOnlineStatus() == localized_online()) && (mSpeakers.find(id) == mSpeakers.end())) { - speakers.emplace_back(speaker_entry_t( + speakers.emplace_back( id, - LLSpeaker::STATUS_VOICE_ACTIVE, - "", LLSpeaker::SPEAKER_AGENT, - (member->getAgentPowers() & GP_SESSION_MODERATOR) == GP_SESSION_MODERATOR)); + LLSpeaker::STATUS_VOICE_ACTIVE, + (member->getAgentPowers() & GP_SESSION_MODERATOR) == GP_SESSION_MODERATOR); } ++member_it; } @@ -638,12 +656,12 @@ void LLSpeakerMgr::updateSpeakerList() } } // Always add the current agent (it has to be there...). Will do nothing if already there. - setSpeaker(gAgentID, "", LLSpeaker::STATUS_VOICE_ACTIVE); + setSpeaker({ gAgentID, LLSpeaker::SPEAKER_AGENT, LLSpeaker::STATUS_VOICE_ACTIVE }); } void LLSpeakerMgr::setSpeakerNotInChannel(LLSpeaker* speakerp) { - speakerp->mStatus = LLSpeaker::STATUS_NOT_IN_CHANNEL; + speakerp->setStatus(LLSpeaker::STATUS_NOT_IN_CHANNEL); speakerp->mDotColor = INACTIVE_COLOR; mSpeakerDelayRemover->setActionTimer(speakerp->mID); } @@ -723,7 +741,7 @@ void LLSpeakerMgr::speakerChatted(const LLUUID& speaker_id) LLPointer speakerp = findSpeaker(speaker_id); if (speakerp.notNull()) { - speakerp->mLastSpokeTime = mSpeechTimer.getElapsedTimeF32(); + speakerp->setSpokenTime(mSpeechTimer.getElapsedTimeF32()); speakerp->mHasSpoken = TRUE; fireEvent(new LLSpeakerUpdateSpeakerEvent(speakerp), "update_speaker"); } @@ -766,8 +784,8 @@ void LLIMSpeakerMgr::setSpeakers(const LLSD& speakers) speaker_it != speakers["agent_info"].endMap(); ++speaker_it) { - bool moderator = false; - bool moderator_muted = false; + boost::optional moderator; + boost::optional moderator_muted; if (speaker_it->second.isMap()) { moderator = speaker_it->second["is_moderator"]; @@ -775,9 +793,8 @@ void LLIMSpeakerMgr::setSpeakers(const LLSD& speakers) } speakerentries.emplace_back( LLUUID(speaker_it->first), - LLSpeaker::STATUS_TEXT_ONLY, - "", LLSpeaker::SPEAKER_AGENT, + LLSpeaker::STATUS_TEXT_ONLY, moderator, moderator_muted ); @@ -816,8 +833,8 @@ void LLIMSpeakerMgr::updateSpeakers(const LLSD& update) LLPointer speakerp = findSpeaker(agent_id); bool new_speaker = false; - bool moderator = false; - bool moderator_muted = false; + boost::optional moderator; + boost::optional moderator_muted_text; LLSD agent_data = update_it->second; if (agent_data.isMap() && agent_data.has("transition")) @@ -852,16 +869,15 @@ void LLIMSpeakerMgr::updateSpeakers(const LLSD& update) } if (agent_info.has("mutes")) { - moderator_muted = agent_info["mutes"]["text"]; + moderator_muted_text = agent_info["mutes"]["text"]; } } speakerentries.emplace_back( agent_id, - LLSpeaker::STATUS_TEXT_ONLY, - "", LLSpeaker::SPEAKER_AGENT, + LLSpeaker::STATUS_TEXT_ONLY, moderator, - moderator_muted + moderator_muted_text ); } } @@ -884,9 +900,7 @@ void LLIMSpeakerMgr::updateSpeakers(const LLSD& update) else if ( agent_transition == "ENTER") { // add or update speaker - speakerentries.emplace_back( - agent_id - ); + speakerentries.emplace_back(agent_id); } else { @@ -1074,8 +1088,8 @@ void LLLocalSpeakerMgr::updateSpeakerList() 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 && speakerp->mStatus == LLSpeaker::STATUS_TEXT_ONLY) + LLPointer speakerp = speaker_it->second; + if (speakerp.notNull() && speakerp->mStatus == LLSpeaker::STATUS_TEXT_ONLY) { LLVOAvatar* avatarp = gObjectList.findAvatar(speaker_id); if (!avatarp || dist_vec_squared(avatarp->getPositionAgent(), gAgent.getPositionAgent()) > CHAT_NORMAL_RADIUS * CHAT_NORMAL_RADIUS) diff --git a/indra/newview/llspeakers.h b/indra/newview/llspeakers.h index a2f05f71a..689ba336f 100644 --- a/indra/newview/llspeakers.h +++ b/indra/newview/llspeakers.h @@ -56,29 +56,78 @@ public: STATUS_MUTED } ESpeakerStatus; + struct speaker_entry_t + { + speaker_entry_t(const LLUUID& id, + LLSpeaker::ESpeakerType type = ESpeakerType::SPEAKER_AGENT, + LLSpeaker::ESpeakerStatus status = ESpeakerStatus::STATUS_TEXT_ONLY, + const boost::optional moderator = boost::none, + const boost::optional moderator_muted_text = boost::none, + std::string name = std::string()) : + id(id), + type(type), + status(status), + moderator(moderator), + moderator_muted_text(moderator_muted_text), + name(name) + {} + const LLUUID id; + const LLSpeaker::ESpeakerType type; + const LLSpeaker::ESpeakerStatus status; + const boost::optional moderator; + const boost::optional moderator_muted_text; + const std::string name; + }; - LLSpeaker(const LLUUID& id, const std::string& name = LLStringUtil::null, const ESpeakerType type = SPEAKER_AGENT); + LLSpeaker(const speaker_entry_t& entry); ~LLSpeaker() {}; + void update(const speaker_entry_t& entry); void lookupName(); void onNameCache(const LLAvatarName& full_name); bool isInVoiceChannel(); + void setStatus(ESpeakerStatus status) + { + if (status != mStatus) + { + mStatus = status; + mNeedsResort = true; + } + } + void setName(const std::string& name) + { + if (name != mDisplayName) + { + mDisplayName = name; + mNeedsResort = true; + } + } + void setSpokenTime(F32 time) + { + if (mLastSpokeTime != time) + { + mLastSpokeTime = time; + mNeedsResort = true; + } + } + + LLUUID mID; ESpeakerStatus mStatus; // current activity status in speech group + ESpeakerType mType : 2; + bool mIsModerator : 1; + bool mModeratorMutedVoice : 1; + bool mModeratorMutedText : 1; + bool mHasSpoken : 1; // has this speaker said anything this session? + bool mHasLeftCurrentCall : 1; // has this speaker left the current voice call? + bool mTyping : 1; 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 - 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; + bool mNeedsResort; S32 mSortIndex; - ESpeakerType mType; - BOOL mIsModerator; - BOOL mModeratorMutedVoice; - BOOL mModeratorMutedText; }; class LLSpeakerUpdateSpeakerEvent : public LLOldEvents::LLEvent @@ -137,7 +186,7 @@ private: class LLSpeakerActionTimer : public LLEventTimer { public: - typedef boost::function action_callback_t; + typedef std::function action_callback_t; typedef std::map action_timers_map_t; typedef action_timers_map_t::value_type action_value_t; typedef action_timers_map_t::const_iterator action_timer_const_iter_t; @@ -223,6 +272,8 @@ class LLSpeakerMgr : public LLOldEvents::LLObservable LOG_CLASS(LLSpeakerMgr); public: + typedef LLSpeaker::speaker_entry_t speaker_entry_t; + LLSpeakerMgr(LLVoiceChannel* channelp); virtual ~LLSpeakerMgr(); @@ -231,31 +282,8 @@ public: void setSpeakerTyping(const LLUUID& speaker_id, BOOL typing); void speakerChatted(const LLUUID& speaker_id); - struct speaker_entry_t - { - speaker_entry_t(const LLUUID& id, - LLSpeaker::ESpeakerStatus status = LLSpeaker::STATUS_TEXT_ONLY, - const std::string& name = LLStringUtil::null, - LLSpeaker::ESpeakerType type = LLSpeaker::SPEAKER_AGENT, - const bool moderator = false, - const bool moderator_muted_text = false) : - id(id), name(name), status(status), type(type), moderator(moderator), moderator_muted_text(moderator_muted_text) - {} - const LLUUID id; - const std::string name; - const LLSpeaker::ESpeakerStatus status; - const LLSpeaker::ESpeakerType type; - const bool moderator; - const bool moderator_muted_text; - }; - void setSpeakers(const std::vector& speakers); - LLPointer setSpeaker(const LLUUID& id, - const std::string& name = LLStringUtil::null, - LLSpeaker::ESpeakerStatus status = LLSpeaker::STATUS_TEXT_ONLY, - LLSpeaker::ESpeakerType = LLSpeaker::SPEAKER_AGENT, - bool moderator = false, - bool moderator_muted_text = false); + LLPointer setSpeaker(const speaker_entry_t& speakers); BOOL isVoiceActive();