diff options
author | Ansariel <ansariel.hiller@phoenixviewer.com> | 2024-05-22 19:04:52 +0200 |
---|---|---|
committer | Ansariel <ansariel.hiller@phoenixviewer.com> | 2024-05-22 19:04:52 +0200 |
commit | 1b67dd855c41f5a0cda7ec2a68d98071986ca703 (patch) | |
tree | ab243607f74f78200787bba5b9b88f07ef1b966f /indra/newview/llconversationmodel.cpp | |
parent | 6d6eabca44d08d5b97bfe3e941d2b9687c2246ea (diff) | |
parent | e1623bb276f83a43ce7a197e388720c05bdefe61 (diff) |
Merge remote-tracking branch 'origin/main' into DRTVWR-600-maint-A
# Conflicts:
# autobuild.xml
# indra/cmake/CMakeLists.txt
# indra/cmake/GoogleMock.cmake
# indra/llaudio/llaudioengine_fmodstudio.cpp
# indra/llaudio/llaudioengine_fmodstudio.h
# indra/llaudio/lllistener_fmodstudio.cpp
# indra/llaudio/lllistener_fmodstudio.h
# indra/llaudio/llstreamingaudio_fmodstudio.cpp
# indra/llaudio/llstreamingaudio_fmodstudio.h
# indra/llcharacter/llmultigesture.cpp
# indra/llcharacter/llmultigesture.h
# indra/llimage/llimage.cpp
# indra/llimage/llimagepng.cpp
# indra/llimage/llimageworker.cpp
# indra/llimage/tests/llimageworker_test.cpp
# indra/llmessage/tests/llmockhttpclient.h
# indra/llprimitive/llgltfmaterial.h
# indra/llrender/llfontfreetype.cpp
# indra/llui/llcombobox.cpp
# indra/llui/llfolderview.cpp
# indra/llui/llfolderviewmodel.h
# indra/llui/lllineeditor.cpp
# indra/llui/lllineeditor.h
# indra/llui/lltextbase.cpp
# indra/llui/lltextbase.h
# indra/llui/lltexteditor.cpp
# indra/llui/lltextvalidate.cpp
# indra/llui/lltextvalidate.h
# indra/llui/lluictrl.h
# indra/llui/llview.cpp
# indra/llwindow/llwindowmacosx.cpp
# indra/newview/app_settings/settings.xml
# indra/newview/llappearancemgr.cpp
# indra/newview/llappearancemgr.h
# indra/newview/llavatarpropertiesprocessor.cpp
# indra/newview/llavatarpropertiesprocessor.h
# indra/newview/llbreadcrumbview.cpp
# indra/newview/llbreadcrumbview.h
# indra/newview/llbreastmotion.cpp
# indra/newview/llbreastmotion.h
# indra/newview/llconversationmodel.h
# indra/newview/lldensityctrl.cpp
# indra/newview/lldensityctrl.h
# indra/newview/llface.inl
# indra/newview/llfloatereditsky.cpp
# indra/newview/llfloatereditwater.cpp
# indra/newview/llfloateremojipicker.h
# indra/newview/llfloaterimsessiontab.cpp
# indra/newview/llfloaterprofiletexture.cpp
# indra/newview/llfloaterprofiletexture.h
# indra/newview/llgesturemgr.cpp
# indra/newview/llgesturemgr.h
# indra/newview/llimpanel.cpp
# indra/newview/llimpanel.h
# indra/newview/llinventorybridge.cpp
# indra/newview/llinventorybridge.h
# indra/newview/llinventoryclipboard.cpp
# indra/newview/llinventoryclipboard.h
# indra/newview/llinventoryfunctions.cpp
# indra/newview/llinventoryfunctions.h
# indra/newview/llinventorygallery.cpp
# indra/newview/lllistbrowser.cpp
# indra/newview/lllistbrowser.h
# indra/newview/llpanelobjectinventory.cpp
# indra/newview/llpanelprofile.cpp
# indra/newview/llpanelprofile.h
# indra/newview/llpreviewgesture.cpp
# indra/newview/llsavedsettingsglue.cpp
# indra/newview/llsavedsettingsglue.h
# indra/newview/lltooldraganddrop.cpp
# indra/newview/llurllineeditorctrl.cpp
# indra/newview/llvectorperfoptions.cpp
# indra/newview/llvectorperfoptions.h
# indra/newview/llviewerparceloverlay.cpp
# indra/newview/llviewertexlayer.cpp
# indra/newview/llviewertexturelist.cpp
# indra/newview/macmain.h
# indra/test/test.cpp
Diffstat (limited to 'indra/newview/llconversationmodel.cpp')
-rw-r--r-- | indra/newview/llconversationmodel.cpp | 1530 |
1 files changed, 765 insertions, 765 deletions
diff --git a/indra/newview/llconversationmodel.cpp b/indra/newview/llconversationmodel.cpp index 44d4c863c7..a616f308c7 100644 --- a/indra/newview/llconversationmodel.cpp +++ b/indra/newview/llconversationmodel.cpp @@ -1,765 +1,765 @@ -/** - * @file llconversationmodel.cpp - * @brief Implementation of conversations list - * - * $LicenseInfo:firstyear=2009&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 "llagent.h" -#include "llavatarnamecache.h" -#include "llavataractions.h" -#include "llevents.h" -#include "llfloaterimsession.h" -#include "llsdutil.h" -#include "llconversationmodel.h" -#include "llimview.h" //For LLIMModel -#include "lltrans.h" - -// -// Conversation items : common behaviors -// - -LLConversationItem::LLConversationItem(std::string display_name, const LLUUID& uuid, LLFolderViewModelInterface& root_view_model) : - LLFolderViewModelItemCommon(root_view_model), - mName(display_name), - mUUID(uuid), - mNeedsRefresh(true), - mConvType(CONV_UNKNOWN), - mLastActiveTime(0.0), - mDisplayModeratorOptions(false), - mDisplayGroupBanOptions(false), - mAvatarNameCacheConnection() -{ -} - -LLConversationItem::LLConversationItem(const LLUUID& uuid, LLFolderViewModelInterface& root_view_model) : - LLFolderViewModelItemCommon(root_view_model), - mName(""), - mUUID(uuid), - mNeedsRefresh(true), - mConvType(CONV_UNKNOWN), - mLastActiveTime(0.0), - mDisplayModeratorOptions(false), - mDisplayGroupBanOptions(false), - mAvatarNameCacheConnection() -{ -} - -LLConversationItem::LLConversationItem(LLFolderViewModelInterface& root_view_model) : - LLFolderViewModelItemCommon(root_view_model), - mName(""), - mUUID(), - mNeedsRefresh(true), - mConvType(CONV_UNKNOWN), - mLastActiveTime(0.0), - mDisplayModeratorOptions(false), - mDisplayGroupBanOptions(false), - mAvatarNameCacheConnection() -{ -} - -LLConversationItem::~LLConversationItem() -{ - // Disconnect any previous avatar name cache connection to ensure - // that the callback method is not called after destruction - if (mAvatarNameCacheConnection.connected()) - { - mAvatarNameCacheConnection.disconnect(); - } - - clearChildren(); -} - -//virtual -void LLConversationItem::addChild(LLFolderViewModelItem* child) -{ - // Avoid duplicates: bail out if that child is already present in the list - // Note: this happens when models are created and 'parented' before views - // This is performance unfriendly, but conversation can addToFolder multiple times - child_list_t::const_iterator iter; - for (iter = mChildren.begin(); iter != mChildren.end(); iter++) - { - if (child == *iter) - { - return; - } - } - LLFolderViewModelItemCommon::addChild(child); -} - -void LLConversationItem::postEvent(const std::string& event_type, LLConversationItemSession* session, LLConversationItemParticipant* participant) -{ - LLUUID session_id = (session ? session->getUUID() : LLUUID()); - LLUUID participant_id = (participant ? participant->getUUID() : LLUUID()); - LLSD event(LLSDMap("type", event_type)("session_uuid", session_id)("participant_uuid", participant_id)); - LLEventPumps::instance().obtain("ConversationsEvents").post(event); -} - -// Virtual action callbacks -void LLConversationItem::performAction(LLInventoryModel* model, std::string action) -{ -} - -void LLConversationItem::openItem( void ) -{ -} - -void LLConversationItem::closeItem( void ) -{ -} - -void LLConversationItem::previewItem( void ) -{ -} - -void LLConversationItem::showProperties(void) -{ -} - -void LLConversationItem::buildParticipantMenuOptions(menuentry_vec_t& items, U32 flags) -{ - if (flags & ITEM_IN_MULTI_SELECTION) - { - items.push_back(std::string("im")); - items.push_back(std::string("offer_teleport")); - items.push_back(std::string("voice_call")); - items.push_back(std::string("remove_friends")); - } - else - { - items.push_back(std::string("view_profile")); - items.push_back(std::string("im")); - items.push_back(std::string("offer_teleport")); - items.push_back(std::string("request_teleport")); - - if (getType() != CONV_SESSION_1_ON_1) - { - items.push_back(std::string("voice_call")); - } - else - { - LLVoiceChannel* voice_channel = LLIMModel::getInstance() ? LLIMModel::getInstance()->getVoiceChannel(this->getUUID()) : NULL; - if(voice_channel != LLVoiceChannel::getCurrentVoiceChannel()) - { - items.push_back(std::string("voice_call")); - } - else - { - items.push_back(std::string("disconnect_from_voice")); - } - } - - items.push_back(std::string("chat_history")); - items.push_back(std::string("separator_chat_history")); - items.push_back(std::string("add_friend")); - items.push_back(std::string("remove_friend")); - items.push_back(std::string("invite_to_group")); - items.push_back(std::string("separator_invite_to_group")); - if (static_cast<LLConversationItem*>(mParent)->getType() == CONV_SESSION_NEARBY) - items.push_back(std::string("zoom_in")); - items.push_back(std::string("map")); - items.push_back(std::string("share")); - items.push_back(std::string("pay")); - items.push_back(std::string("report_abuse")); - items.push_back(std::string("block_unblock")); - items.push_back(std::string("MuteText")); - - if ((getType() != CONV_SESSION_1_ON_1) && mDisplayModeratorOptions) - { - items.push_back(std::string("Moderator Options Separator")); - items.push_back(std::string("Moderator Options")); - items.push_back(std::string("AllowTextChat")); - items.push_back(std::string("moderate_voice_separator")); - items.push_back(std::string("ModerateVoiceMuteSelected")); - items.push_back(std::string("ModerateVoiceUnMuteSelected")); - items.push_back(std::string("ModerateVoiceMute")); - items.push_back(std::string("ModerateVoiceUnmute")); - } - - if ((getType() != CONV_SESSION_1_ON_1) && mDisplayGroupBanOptions) - { - items.push_back(std::string("Group Ban Separator")); - items.push_back(std::string("BanMember")); - } - } -} - -// method does subscription to changes in avatar name cache for current session/participant conversation item. -void LLConversationItem::fetchAvatarName(bool isParticipant /*= true*/) -{ - LLUUID item_id = getUUID(); - - // item should not be null for participants - if (isParticipant) - { - llassert(item_id.notNull()); - } - - // disconnect any previous avatar name cache connection - if (mAvatarNameCacheConnection.connected()) - { - mAvatarNameCacheConnection.disconnect(); - } - - // exclude nearby chat item - if (item_id.notNull()) - { - // for P2P session item, override it as item of called agent - if (CONV_SESSION_1_ON_1 == getType()) - { - item_id = LLIMModel::getInstance()->getOtherParticipantID(item_id); - } - - // subscribe on avatar name cache changes for participant and session items - mAvatarNameCacheConnection = LLAvatarNameCache::get(item_id, boost::bind(&LLConversationItem::onAvatarNameCache, this, _2)); - } -} - -// -// LLConversationItemSession -// - -LLConversationItemSession::LLConversationItemSession(std::string display_name, const LLUUID& uuid, LLFolderViewModelInterface& root_view_model) : - LLConversationItem(display_name,uuid,root_view_model), - mIsLoaded(false) -{ - mConvType = CONV_SESSION_UNKNOWN; -} - -LLConversationItemSession::LLConversationItemSession(const LLUUID& uuid, LLFolderViewModelInterface& root_view_model) : - LLConversationItem(uuid,root_view_model) -{ - mConvType = CONV_SESSION_UNKNOWN; -} - -LLConversationItemSession::~LLConversationItemSession() -{ - clearAndDeparentModels(); -} - -bool LLConversationItemSession::hasChildren() const -{ - return getChildrenCount() > 0; -} - -void LLConversationItemSession::addParticipant(LLConversationItemParticipant* participant) -{ - addChild(participant); - mIsLoaded = true; - mNeedsRefresh = true; - updateName(participant); - postEvent("add_participant", this, participant); -} - -void LLConversationItemSession::updateName(LLConversationItemParticipant* participant) -{ - EConversationType conversation_type = getType(); - // We modify the session name only in the case of an ad-hoc session or P2P session, exit otherwise (nothing to do) - if ((conversation_type != CONV_SESSION_AD_HOC) && (conversation_type != CONV_SESSION_1_ON_1)) - { - return; - } - - // Avoid changing the default name if no participant present yet - if (mChildren.size() == 0) - { - return; - } - - uuid_vec_t temp_uuids; // uuids vector for building the added participants' names string - if (conversation_type == CONV_SESSION_AD_HOC || conversation_type == CONV_SESSION_1_ON_1) - { - // Build a string containing the participants UUIDs (minus own agent) and check if ready for display (we don't want "(waiting)" in there) - // Note: we don't bind ourselves to the LLAvatarNameCache event as updateParticipantName() is called by - // onAvatarNameCache() which is itself attached to the same event. - - // In the case of a P2P conversation, we need to grab the name of the other participant in the session instance itself - // as we do not create participants for such a session. - - for (auto itemp : mChildren) - { - LLConversationItem* current_participant = dynamic_cast<LLConversationItem*>(itemp); - // Add the avatar uuid to the list (except if it's the own agent uuid) - if (current_participant->getUUID() != gAgentID) - { - LLAvatarName av_name; - if (LLAvatarNameCache::get(current_participant->getUUID(), &av_name)) - { - temp_uuids.push_back(current_participant->getUUID()); - - if (conversation_type == CONV_SESSION_1_ON_1) - { - break; - } - } - } - } - } - - if (temp_uuids.size() != 0) - { - std::string new_session_name; - LLAvatarActions::buildResidentsString(temp_uuids, new_session_name); - renameItem(new_session_name); - postEvent("update_session", this, NULL); - } -} - -void LLConversationItemSession::removeParticipant(LLConversationItemParticipant* participant) -{ - removeChild(participant); - mNeedsRefresh = true; - updateName(participant); - postEvent("remove_participant", this, participant); -} - -void LLConversationItemSession::removeParticipant(const LLUUID& participant_id) -{ - LLConversationItemParticipant* participant = findParticipant(participant_id); - if (participant) - { - removeParticipant(participant); - } -} - -void LLConversationItemSession::clearParticipants() -{ - // clearParticipants function potentially is malfunctioning since it only cleans children of models, - // it does nothing to views that own those models (listeners) - // probably needs to post some kind of 'remove all participants' event - clearChildren(); - mIsLoaded = false; - mNeedsRefresh = true; -} - - -void LLConversationItemSession::clearAndDeparentModels() -{ - for (LLFolderViewModelItem* child : mChildren) - { - if (child->getNumRefs() == 0) - { - // LLConversationItemParticipant can be created but not assigned to any view, - // it was waiting for an "add_participant" event to be processed - delete child; - } - else - { - // Model is still assigned to some view/widget - child->setParent(NULL); - } - } - mChildren.clear(); -} - -LLConversationItemParticipant* LLConversationItemSession::findParticipant(const LLUUID& participant_id) -{ - // This is *not* a general tree parsing algorithm. It assumes that a session contains only - // items (LLConversationItemParticipant) that have themselve no children. - LLConversationItemParticipant* participant = NULL; - child_list_t::iterator iter; - for (iter = mChildren.begin(); iter != mChildren.end(); iter++) - { - participant = dynamic_cast<LLConversationItemParticipant*>(*iter); - if (participant && participant->hasSameValue(participant_id)) - { - break; - } - } - return (iter == mChildren.end() ? NULL : participant); -} - -void LLConversationItemSession::setParticipantIsMuted(const LLUUID& participant_id, bool is_muted) -{ - LLConversationItemParticipant* participant = findParticipant(participant_id); - if (participant) - { - participant->moderateVoice(is_muted); - } -} - -void LLConversationItemSession::setParticipantIsModerator(const LLUUID& participant_id, bool is_moderator) -{ - LLConversationItemParticipant* participant = findParticipant(participant_id); - if (participant) - { - participant->setIsModerator(is_moderator); - } -} - -void LLConversationItemSession::setTimeNow(const LLUUID& participant_id) -{ - mLastActiveTime = LLFrameTimer::getElapsedSeconds(); - mNeedsRefresh = true; - LLConversationItemParticipant* participant = findParticipant(participant_id); - if (participant) - { - participant->setTimeNow(); - } -} - -void LLConversationItemSession::setDistance(const LLUUID& participant_id, F64 dist) -{ - LLConversationItemParticipant* participant = findParticipant(participant_id); - if (participant) - { - participant->setDistance(dist); - mNeedsRefresh = true; - } -} - -void LLConversationItemSession::buildContextMenu(LLMenuGL& menu, U32 flags) -{ - LL_DEBUGS() << "LLConversationItemParticipant::buildContextMenu()" << LL_ENDL; - menuentry_vec_t items; - menuentry_vec_t disabled_items; - if((flags & ITEM_IN_MULTI_SELECTION) && (this->getType() != CONV_SESSION_NEARBY)) - { - items.push_back(std::string("close_selected_conversations")); - } - if(this->getType() == CONV_SESSION_1_ON_1) - { - items.push_back(std::string("close_conversation")); - items.push_back(std::string("separator_disconnect_from_voice")); - buildParticipantMenuOptions(items, flags); - } - else if(this->getType() == CONV_SESSION_GROUP) - { - items.push_back(std::string("close_conversation")); - addVoiceOptions(items); - items.push_back(std::string("chat_history")); - items.push_back(std::string("separator_chat_history")); - items.push_back(std::string("group_profile")); - items.push_back(std::string("activate_group")); - items.push_back(std::string("leave_group")); - } - else if(this->getType() == CONV_SESSION_AD_HOC) - { - items.push_back(std::string("close_conversation")); - addVoiceOptions(items); - items.push_back(std::string("chat_history")); - } - else if(this->getType() == CONV_SESSION_NEARBY) - { - items.push_back(std::string("chat_history")); - } - - hide_context_entries(menu, items, disabled_items); -} - -void LLConversationItemSession::addVoiceOptions(menuentry_vec_t& items) -{ - LLVoiceChannel* voice_channel = LLIMModel::getInstance() ? LLIMModel::getInstance()->getVoiceChannel(this->getUUID()) : NULL; - - if(voice_channel != LLVoiceChannel::getCurrentVoiceChannel()) - { - items.push_back(std::string("open_voice_conversation")); - } - else - { - items.push_back(std::string("disconnect_from_voice")); - } -} - -// The time of activity of a session is the time of the most recent activity, session and participants included -const bool LLConversationItemSession::getTime(F64& time) const -{ - F64 most_recent_time = mLastActiveTime; - bool has_time = (most_recent_time > 0.1); - LLConversationItemParticipant* participant = NULL; - child_list_t::const_iterator iter; - for (iter = mChildren.begin(); iter != mChildren.end(); iter++) - { - participant = dynamic_cast<LLConversationItemParticipant*>(*iter); - F64 participant_time; - if (participant && participant->getTime(participant_time)) - { - has_time = true; - most_recent_time = llmax(most_recent_time,participant_time); - } - } - if (has_time) - { - time = most_recent_time; - } - return has_time; -} - -void LLConversationItemSession::dumpDebugData(bool dump_children) -{ - // Session info - LL_INFOS() << "Merov debug : session " << this << ", uuid = " << mUUID << ", name = " << mName << ", is loaded = " << mIsLoaded << LL_ENDL; - // Children info - if (dump_children) - { - for (child_list_t::iterator iter = mChildren.begin(); iter != mChildren.end(); iter++) - { - LLConversationItemParticipant* participant = dynamic_cast<LLConversationItemParticipant*>(*iter); - if (participant) - { - participant->dumpDebugData(); - } - } - } -} - -// should be invoked only for P2P sessions -void LLConversationItemSession::onAvatarNameCache(const LLAvatarName& av_name) -{ - if (mAvatarNameCacheConnection.connected()) - { - mAvatarNameCacheConnection.disconnect(); - } - - renameItem(av_name.getDisplayName()); - postEvent("update_session", this, NULL); -} - -// -// LLConversationItemParticipant -// - -LLConversationItemParticipant::LLConversationItemParticipant(std::string display_name, const LLUUID& uuid, LLFolderViewModelInterface& root_view_model) : - LLConversationItem(display_name,uuid,root_view_model), - mIsModeratorMuted(false), - mIsModerator(false), - mDisplayModeratorLabel(false), - mDistToAgent(-1.0) -{ - mDisplayName = display_name; - mConvType = CONV_PARTICIPANT; -} - -LLConversationItemParticipant::LLConversationItemParticipant(const LLUUID& uuid, LLFolderViewModelInterface& root_view_model) : - LLConversationItem(uuid,root_view_model), - mIsModeratorMuted(false), - mIsModerator(false), - mDisplayModeratorLabel(false), - mDistToAgent(-1.0) -{ - mConvType = CONV_PARTICIPANT; -} - -void LLConversationItemParticipant::updateName() -{ - llassert(getUUID().notNull()); - if (getUUID().notNull()) - { - LLAvatarName av_name; - if (LLAvatarNameCache::get(getUUID(),&av_name)) - { - updateName(av_name); - } - } -} - -void LLConversationItemParticipant::onAvatarNameCache(const LLAvatarName& av_name) -{ - if (mAvatarNameCacheConnection.connected()) - { - mAvatarNameCacheConnection.disconnect(); - } - - updateName(av_name); -} - -void LLConversationItemParticipant::updateName(const LLAvatarName& av_name) -{ - mName = av_name.getUserName(); - mDisplayName = av_name.getDisplayName(); - - if (mDisplayModeratorLabel) - { - mDisplayName += " " + LLTrans::getString("IM_moderator_label"); - } - - renameItem(mDisplayName); - if (mParent != NULL) - { - LLConversationItemSession* parent_session = dynamic_cast<LLConversationItemSession*>(mParent); - if (parent_session != NULL) - { - parent_session->requestSort(); - parent_session->updateName(this); - postEvent("update_participant", parent_session, this); - } - } -} - -void LLConversationItemParticipant::buildContextMenu(LLMenuGL& menu, U32 flags) -{ - menuentry_vec_t items; - menuentry_vec_t disabled_items; - - buildParticipantMenuOptions(items, flags); - - hide_context_entries(menu, items, disabled_items); -} - -LLConversationItemSession* LLConversationItemParticipant::getParentSession() -{ - LLConversationItemSession* parent_session = NULL; - if (hasParent()) - { - parent_session = dynamic_cast<LLConversationItemSession*>(mParent); - } - return parent_session; -} - -void LLConversationItemParticipant::dumpDebugData() -{ - LL_INFOS() << "Merov debug : participant, uuid = " << mUUID << ", name = " << mName << ", display name = " << mDisplayName << ", muted = " << isVoiceMuted() << ", moderator = " << mIsModerator << LL_ENDL; -} - -void LLConversationItemParticipant::setDisplayModeratorRole(bool displayRole) -{ - if (displayRole != mDisplayModeratorLabel) - { - mDisplayModeratorLabel = displayRole; - updateName(); - } -} - -bool LLConversationItemParticipant::isVoiceMuted() -{ - return mIsModeratorMuted || LLMuteList::getInstance()->isMuted(mUUID, LLMute::flagVoiceChat); -} - -// -// LLConversationSort -// - -// Comparison operator: returns "true" is a comes before b, "false" otherwise -bool LLConversationSort::operator()(const LLConversationItem* const& a, const LLConversationItem* const& b) const -{ - LLConversationItem::EConversationType type_a = a->getType(); - LLConversationItem::EConversationType type_b = b->getType(); - - if ((type_a == LLConversationItem::CONV_PARTICIPANT) && (type_b == LLConversationItem::CONV_PARTICIPANT)) - { - // If both items are participants - U32 sort_order = getSortOrderParticipants(); - if (sort_order == LLConversationFilter::SO_DATE) - { - F64 time_a = 0.0; - F64 time_b = 0.0; - bool has_time_a = a->getTime(time_a); - bool has_time_b = b->getTime(time_b); - if (has_time_a && has_time_b) - { - // Most recent comes first - return (time_a > time_b); - } - else if (has_time_a || has_time_b) - { - // If we have only one time available, the element with time must come first - return has_time_a; - } - // If no time available, we'll default to sort by name at the end of this method - } - else if (sort_order == LLConversationFilter::SO_DISTANCE) - { - F64 dist_a = 0.0; - F64 dist_b = 0.0; - bool has_dist_a = a->getDistanceToAgent(dist_a); - bool has_dist_b = b->getDistanceToAgent(dist_b); - if (has_dist_a && has_dist_b) - { - // Closest comes first - return (dist_a < dist_b); - } - else if (has_dist_a || has_dist_b) - { - // If we have only one distance available, the element with it must come first - return has_dist_a; - } - // If no distance available, we'll default to sort by name at the end of this method - } - } - else if ((type_a > LLConversationItem::CONV_PARTICIPANT) && (type_b > LLConversationItem::CONV_PARTICIPANT)) - { - // If both are sessions - U32 sort_order = getSortOrderSessions(); - - if (sort_order == LLConversationFilter::SO_DATE) - { - // Sort by time - F64 time_a = 0.0; - F64 time_b = 0.0; - bool has_time_a = a->getTime(time_a); - bool has_time_b = b->getTime(time_b); - if (has_time_a && has_time_b) - { - // Most recent comes first - return (time_a > time_b); - } - else if (has_time_a || has_time_b) - { - // If we have only one time available, the element with time must come first - return has_time_a; - } - // If no time available, we'll default to sort by name at the end of this method - } - else - { - if ((type_a == LLConversationItem::CONV_SESSION_NEARBY) || (type_b == LLConversationItem::CONV_SESSION_NEARBY)) - { - // If one is the nearby session, put nearby session *always* last - return (type_b == LLConversationItem::CONV_SESSION_NEARBY); - } - else if (sort_order == LLConversationFilter::SO_SESSION_TYPE) - { - if (type_a != type_b) - { - // Lowest types come first. See LLConversationItem definition of types - return (type_a < type_b); - } - // If types are identical, we'll default to sort by name at the end of this method - } - } - } - else - { - // If one item is a participant and the other a session, the session comes before the participant - // so we simply compare the type - // Notes: as a consequence, CONV_UNKNOWN (which should never get created...) always come first - return (type_a > type_b); - } - // By default, in all other possible cases (including sort order type LLConversationFilter::SO_NAME of course), - // we sort by name - S32 compare = LLStringUtil::compareDict(a->getName(), b->getName()); - return (compare < 0); -} - -// -// LLConversationViewModel -// - -void LLConversationViewModel::sort(LLFolderViewFolder* folder) -{ - base_t::sort(folder); -} - -// EOF +/**
+ * @file llconversationmodel.cpp
+ * @brief Implementation of conversations list
+ *
+ * $LicenseInfo:firstyear=2009&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 "llagent.h"
+#include "llavatarnamecache.h"
+#include "llavataractions.h"
+#include "llevents.h"
+#include "llfloaterimsession.h"
+#include "llsdutil.h"
+#include "llconversationmodel.h"
+#include "llimview.h" //For LLIMModel
+#include "lltrans.h"
+
+//
+// Conversation items : common behaviors
+//
+
+LLConversationItem::LLConversationItem(std::string display_name, const LLUUID& uuid, LLFolderViewModelInterface& root_view_model) :
+ LLFolderViewModelItemCommon(root_view_model),
+ mName(display_name),
+ mUUID(uuid),
+ mNeedsRefresh(true),
+ mConvType(CONV_UNKNOWN),
+ mLastActiveTime(0.0),
+ mDisplayModeratorOptions(false),
+ mDisplayGroupBanOptions(false),
+ mAvatarNameCacheConnection()
+{
+}
+
+LLConversationItem::LLConversationItem(const LLUUID& uuid, LLFolderViewModelInterface& root_view_model) :
+ LLFolderViewModelItemCommon(root_view_model),
+ mName(""),
+ mUUID(uuid),
+ mNeedsRefresh(true),
+ mConvType(CONV_UNKNOWN),
+ mLastActiveTime(0.0),
+ mDisplayModeratorOptions(false),
+ mDisplayGroupBanOptions(false),
+ mAvatarNameCacheConnection()
+{
+}
+
+LLConversationItem::LLConversationItem(LLFolderViewModelInterface& root_view_model) :
+ LLFolderViewModelItemCommon(root_view_model),
+ mName(""),
+ mUUID(),
+ mNeedsRefresh(true),
+ mConvType(CONV_UNKNOWN),
+ mLastActiveTime(0.0),
+ mDisplayModeratorOptions(false),
+ mDisplayGroupBanOptions(false),
+ mAvatarNameCacheConnection()
+{
+}
+
+LLConversationItem::~LLConversationItem()
+{
+ // Disconnect any previous avatar name cache connection to ensure
+ // that the callback method is not called after destruction
+ if (mAvatarNameCacheConnection.connected())
+ {
+ mAvatarNameCacheConnection.disconnect();
+ }
+
+ clearChildren();
+}
+
+//virtual
+void LLConversationItem::addChild(LLFolderViewModelItem* child)
+{
+ // Avoid duplicates: bail out if that child is already present in the list
+ // Note: this happens when models are created and 'parented' before views
+ // This is performance unfriendly, but conversation can addToFolder multiple times
+ child_list_t::const_iterator iter;
+ for (iter = mChildren.begin(); iter != mChildren.end(); iter++)
+ {
+ if (child == *iter)
+ {
+ return;
+ }
+ }
+ LLFolderViewModelItemCommon::addChild(child);
+}
+
+void LLConversationItem::postEvent(const std::string& event_type, LLConversationItemSession* session, LLConversationItemParticipant* participant)
+{
+ LLUUID session_id = (session ? session->getUUID() : LLUUID());
+ LLUUID participant_id = (participant ? participant->getUUID() : LLUUID());
+ LLSD event(LLSDMap("type", event_type)("session_uuid", session_id)("participant_uuid", participant_id));
+ LLEventPumps::instance().obtain("ConversationsEvents").post(event);
+}
+
+// Virtual action callbacks
+void LLConversationItem::performAction(LLInventoryModel* model, std::string action)
+{
+}
+
+void LLConversationItem::openItem( void )
+{
+}
+
+void LLConversationItem::closeItem( void )
+{
+}
+
+void LLConversationItem::previewItem( void )
+{
+}
+
+void LLConversationItem::showProperties(void)
+{
+}
+
+void LLConversationItem::buildParticipantMenuOptions(menuentry_vec_t& items, U32 flags)
+{
+ if (flags & ITEM_IN_MULTI_SELECTION)
+ {
+ items.push_back(std::string("im"));
+ items.push_back(std::string("offer_teleport"));
+ items.push_back(std::string("voice_call"));
+ items.push_back(std::string("remove_friends"));
+ }
+ else
+ {
+ items.push_back(std::string("view_profile"));
+ items.push_back(std::string("im"));
+ items.push_back(std::string("offer_teleport"));
+ items.push_back(std::string("request_teleport"));
+
+ if (getType() != CONV_SESSION_1_ON_1)
+ {
+ items.push_back(std::string("voice_call"));
+ }
+ else
+ {
+ LLVoiceChannel* voice_channel = LLIMModel::getInstance() ? LLIMModel::getInstance()->getVoiceChannel(this->getUUID()) : NULL;
+ if(voice_channel != LLVoiceChannel::getCurrentVoiceChannel())
+ {
+ items.push_back(std::string("voice_call"));
+ }
+ else
+ {
+ items.push_back(std::string("disconnect_from_voice"));
+ }
+ }
+
+ items.push_back(std::string("chat_history"));
+ items.push_back(std::string("separator_chat_history"));
+ items.push_back(std::string("add_friend"));
+ items.push_back(std::string("remove_friend"));
+ items.push_back(std::string("invite_to_group"));
+ items.push_back(std::string("separator_invite_to_group"));
+ if (static_cast<LLConversationItem*>(mParent)->getType() == CONV_SESSION_NEARBY)
+ items.push_back(std::string("zoom_in"));
+ items.push_back(std::string("map"));
+ items.push_back(std::string("share"));
+ items.push_back(std::string("pay"));
+ items.push_back(std::string("report_abuse"));
+ items.push_back(std::string("block_unblock"));
+ items.push_back(std::string("MuteText"));
+
+ if ((getType() != CONV_SESSION_1_ON_1) && mDisplayModeratorOptions)
+ {
+ items.push_back(std::string("Moderator Options Separator"));
+ items.push_back(std::string("Moderator Options"));
+ items.push_back(std::string("AllowTextChat"));
+ items.push_back(std::string("moderate_voice_separator"));
+ items.push_back(std::string("ModerateVoiceMuteSelected"));
+ items.push_back(std::string("ModerateVoiceUnMuteSelected"));
+ items.push_back(std::string("ModerateVoiceMute"));
+ items.push_back(std::string("ModerateVoiceUnmute"));
+ }
+
+ if ((getType() != CONV_SESSION_1_ON_1) && mDisplayGroupBanOptions)
+ {
+ items.push_back(std::string("Group Ban Separator"));
+ items.push_back(std::string("BanMember"));
+ }
+ }
+}
+
+// method does subscription to changes in avatar name cache for current session/participant conversation item.
+void LLConversationItem::fetchAvatarName(bool isParticipant /*= true*/)
+{
+ LLUUID item_id = getUUID();
+
+ // item should not be null for participants
+ if (isParticipant)
+ {
+ llassert(item_id.notNull());
+ }
+
+ // disconnect any previous avatar name cache connection
+ if (mAvatarNameCacheConnection.connected())
+ {
+ mAvatarNameCacheConnection.disconnect();
+ }
+
+ // exclude nearby chat item
+ if (item_id.notNull())
+ {
+ // for P2P session item, override it as item of called agent
+ if (CONV_SESSION_1_ON_1 == getType())
+ {
+ item_id = LLIMModel::getInstance()->getOtherParticipantID(item_id);
+ }
+
+ // subscribe on avatar name cache changes for participant and session items
+ mAvatarNameCacheConnection = LLAvatarNameCache::get(item_id, boost::bind(&LLConversationItem::onAvatarNameCache, this, _2));
+ }
+}
+
+//
+// LLConversationItemSession
+//
+
+LLConversationItemSession::LLConversationItemSession(std::string display_name, const LLUUID& uuid, LLFolderViewModelInterface& root_view_model) :
+ LLConversationItem(display_name,uuid,root_view_model),
+ mIsLoaded(false)
+{
+ mConvType = CONV_SESSION_UNKNOWN;
+}
+
+LLConversationItemSession::LLConversationItemSession(const LLUUID& uuid, LLFolderViewModelInterface& root_view_model) :
+ LLConversationItem(uuid,root_view_model)
+{
+ mConvType = CONV_SESSION_UNKNOWN;
+}
+
+LLConversationItemSession::~LLConversationItemSession()
+{
+ clearAndDeparentModels();
+}
+
+bool LLConversationItemSession::hasChildren() const
+{
+ return getChildrenCount() > 0;
+}
+
+void LLConversationItemSession::addParticipant(LLConversationItemParticipant* participant)
+{
+ addChild(participant);
+ mIsLoaded = true;
+ mNeedsRefresh = true;
+ updateName(participant);
+ postEvent("add_participant", this, participant);
+}
+
+void LLConversationItemSession::updateName(LLConversationItemParticipant* participant)
+{
+ EConversationType conversation_type = getType();
+ // We modify the session name only in the case of an ad-hoc session or P2P session, exit otherwise (nothing to do)
+ if ((conversation_type != CONV_SESSION_AD_HOC) && (conversation_type != CONV_SESSION_1_ON_1))
+ {
+ return;
+ }
+
+ // Avoid changing the default name if no participant present yet
+ if (mChildren.size() == 0)
+ {
+ return;
+ }
+
+ uuid_vec_t temp_uuids; // uuids vector for building the added participants' names string
+ if (conversation_type == CONV_SESSION_AD_HOC || conversation_type == CONV_SESSION_1_ON_1)
+ {
+ // Build a string containing the participants UUIDs (minus own agent) and check if ready for display (we don't want "(waiting)" in there)
+ // Note: we don't bind ourselves to the LLAvatarNameCache event as updateParticipantName() is called by
+ // onAvatarNameCache() which is itself attached to the same event.
+
+ // In the case of a P2P conversation, we need to grab the name of the other participant in the session instance itself
+ // as we do not create participants for such a session.
+
+ for (auto itemp : mChildren)
+ {
+ LLConversationItem* current_participant = dynamic_cast<LLConversationItem*>(itemp);
+ // Add the avatar uuid to the list (except if it's the own agent uuid)
+ if (current_participant->getUUID() != gAgentID)
+ {
+ LLAvatarName av_name;
+ if (LLAvatarNameCache::get(current_participant->getUUID(), &av_name))
+ {
+ temp_uuids.push_back(current_participant->getUUID());
+
+ if (conversation_type == CONV_SESSION_1_ON_1)
+ {
+ break;
+ }
+ }
+ }
+ }
+ }
+
+ if (temp_uuids.size() != 0)
+ {
+ std::string new_session_name;
+ LLAvatarActions::buildResidentsString(temp_uuids, new_session_name);
+ renameItem(new_session_name);
+ postEvent("update_session", this, NULL);
+ }
+}
+
+void LLConversationItemSession::removeParticipant(LLConversationItemParticipant* participant)
+{
+ removeChild(participant);
+ mNeedsRefresh = true;
+ updateName(participant);
+ postEvent("remove_participant", this, participant);
+}
+
+void LLConversationItemSession::removeParticipant(const LLUUID& participant_id)
+{
+ LLConversationItemParticipant* participant = findParticipant(participant_id);
+ if (participant)
+ {
+ removeParticipant(participant);
+ }
+}
+
+void LLConversationItemSession::clearParticipants()
+{
+ // clearParticipants function potentially is malfunctioning since it only cleans children of models,
+ // it does nothing to views that own those models (listeners)
+ // probably needs to post some kind of 'remove all participants' event
+ clearChildren();
+ mIsLoaded = false;
+ mNeedsRefresh = true;
+}
+
+
+void LLConversationItemSession::clearAndDeparentModels()
+{
+ for (LLFolderViewModelItem* child : mChildren)
+ {
+ if (child->getNumRefs() == 0)
+ {
+ // LLConversationItemParticipant can be created but not assigned to any view,
+ // it was waiting for an "add_participant" event to be processed
+ delete child;
+ }
+ else
+ {
+ // Model is still assigned to some view/widget
+ child->setParent(NULL);
+ }
+ }
+ mChildren.clear();
+}
+
+LLConversationItemParticipant* LLConversationItemSession::findParticipant(const LLUUID& participant_id)
+{
+ // This is *not* a general tree parsing algorithm. It assumes that a session contains only
+ // items (LLConversationItemParticipant) that have themselve no children.
+ LLConversationItemParticipant* participant = NULL;
+ child_list_t::iterator iter;
+ for (iter = mChildren.begin(); iter != mChildren.end(); iter++)
+ {
+ participant = dynamic_cast<LLConversationItemParticipant*>(*iter);
+ if (participant && participant->hasSameValue(participant_id))
+ {
+ break;
+ }
+ }
+ return (iter == mChildren.end() ? NULL : participant);
+}
+
+void LLConversationItemSession::setParticipantIsMuted(const LLUUID& participant_id, bool is_muted)
+{
+ LLConversationItemParticipant* participant = findParticipant(participant_id);
+ if (participant)
+ {
+ participant->moderateVoice(is_muted);
+ }
+}
+
+void LLConversationItemSession::setParticipantIsModerator(const LLUUID& participant_id, bool is_moderator)
+{
+ LLConversationItemParticipant* participant = findParticipant(participant_id);
+ if (participant)
+ {
+ participant->setIsModerator(is_moderator);
+ }
+}
+
+void LLConversationItemSession::setTimeNow(const LLUUID& participant_id)
+{
+ mLastActiveTime = LLFrameTimer::getElapsedSeconds();
+ mNeedsRefresh = true;
+ LLConversationItemParticipant* participant = findParticipant(participant_id);
+ if (participant)
+ {
+ participant->setTimeNow();
+ }
+}
+
+void LLConversationItemSession::setDistance(const LLUUID& participant_id, F64 dist)
+{
+ LLConversationItemParticipant* participant = findParticipant(participant_id);
+ if (participant)
+ {
+ participant->setDistance(dist);
+ mNeedsRefresh = true;
+ }
+}
+
+void LLConversationItemSession::buildContextMenu(LLMenuGL& menu, U32 flags)
+{
+ LL_DEBUGS() << "LLConversationItemParticipant::buildContextMenu()" << LL_ENDL;
+ menuentry_vec_t items;
+ menuentry_vec_t disabled_items;
+ if((flags & ITEM_IN_MULTI_SELECTION) && (this->getType() != CONV_SESSION_NEARBY))
+ {
+ items.push_back(std::string("close_selected_conversations"));
+ }
+ if(this->getType() == CONV_SESSION_1_ON_1)
+ {
+ items.push_back(std::string("close_conversation"));
+ items.push_back(std::string("separator_disconnect_from_voice"));
+ buildParticipantMenuOptions(items, flags);
+ }
+ else if(this->getType() == CONV_SESSION_GROUP)
+ {
+ items.push_back(std::string("close_conversation"));
+ addVoiceOptions(items);
+ items.push_back(std::string("chat_history"));
+ items.push_back(std::string("separator_chat_history"));
+ items.push_back(std::string("group_profile"));
+ items.push_back(std::string("activate_group"));
+ items.push_back(std::string("leave_group"));
+ }
+ else if(this->getType() == CONV_SESSION_AD_HOC)
+ {
+ items.push_back(std::string("close_conversation"));
+ addVoiceOptions(items);
+ items.push_back(std::string("chat_history"));
+ }
+ else if(this->getType() == CONV_SESSION_NEARBY)
+ {
+ items.push_back(std::string("chat_history"));
+ }
+
+ hide_context_entries(menu, items, disabled_items);
+}
+
+void LLConversationItemSession::addVoiceOptions(menuentry_vec_t& items)
+{
+ LLVoiceChannel* voice_channel = LLIMModel::getInstance() ? LLIMModel::getInstance()->getVoiceChannel(this->getUUID()) : NULL;
+
+ if(voice_channel != LLVoiceChannel::getCurrentVoiceChannel())
+ {
+ items.push_back(std::string("open_voice_conversation"));
+ }
+ else
+ {
+ items.push_back(std::string("disconnect_from_voice"));
+ }
+}
+
+// The time of activity of a session is the time of the most recent activity, session and participants included
+const bool LLConversationItemSession::getTime(F64& time) const
+{
+ F64 most_recent_time = mLastActiveTime;
+ bool has_time = (most_recent_time > 0.1);
+ LLConversationItemParticipant* participant = NULL;
+ child_list_t::const_iterator iter;
+ for (iter = mChildren.begin(); iter != mChildren.end(); iter++)
+ {
+ participant = dynamic_cast<LLConversationItemParticipant*>(*iter);
+ F64 participant_time;
+ if (participant && participant->getTime(participant_time))
+ {
+ has_time = true;
+ most_recent_time = llmax(most_recent_time,participant_time);
+ }
+ }
+ if (has_time)
+ {
+ time = most_recent_time;
+ }
+ return has_time;
+}
+
+void LLConversationItemSession::dumpDebugData(bool dump_children)
+{
+ // Session info
+ LL_INFOS() << "Merov debug : session " << this << ", uuid = " << mUUID << ", name = " << mName << ", is loaded = " << mIsLoaded << LL_ENDL;
+ // Children info
+ if (dump_children)
+ {
+ for (child_list_t::iterator iter = mChildren.begin(); iter != mChildren.end(); iter++)
+ {
+ LLConversationItemParticipant* participant = dynamic_cast<LLConversationItemParticipant*>(*iter);
+ if (participant)
+ {
+ participant->dumpDebugData();
+ }
+ }
+ }
+}
+
+// should be invoked only for P2P sessions
+void LLConversationItemSession::onAvatarNameCache(const LLAvatarName& av_name)
+{
+ if (mAvatarNameCacheConnection.connected())
+ {
+ mAvatarNameCacheConnection.disconnect();
+ }
+
+ renameItem(av_name.getDisplayName());
+ postEvent("update_session", this, NULL);
+}
+
+//
+// LLConversationItemParticipant
+//
+
+LLConversationItemParticipant::LLConversationItemParticipant(std::string display_name, const LLUUID& uuid, LLFolderViewModelInterface& root_view_model) :
+ LLConversationItem(display_name,uuid,root_view_model),
+ mIsModeratorMuted(false),
+ mIsModerator(false),
+ mDisplayModeratorLabel(false),
+ mDistToAgent(-1.0)
+{
+ mDisplayName = display_name;
+ mConvType = CONV_PARTICIPANT;
+}
+
+LLConversationItemParticipant::LLConversationItemParticipant(const LLUUID& uuid, LLFolderViewModelInterface& root_view_model) :
+ LLConversationItem(uuid,root_view_model),
+ mIsModeratorMuted(false),
+ mIsModerator(false),
+ mDisplayModeratorLabel(false),
+ mDistToAgent(-1.0)
+{
+ mConvType = CONV_PARTICIPANT;
+}
+
+void LLConversationItemParticipant::updateName()
+{
+ llassert(getUUID().notNull());
+ if (getUUID().notNull())
+ {
+ LLAvatarName av_name;
+ if (LLAvatarNameCache::get(getUUID(),&av_name))
+ {
+ updateName(av_name);
+ }
+ }
+}
+
+void LLConversationItemParticipant::onAvatarNameCache(const LLAvatarName& av_name)
+{
+ if (mAvatarNameCacheConnection.connected())
+ {
+ mAvatarNameCacheConnection.disconnect();
+ }
+
+ updateName(av_name);
+}
+
+void LLConversationItemParticipant::updateName(const LLAvatarName& av_name)
+{
+ mName = av_name.getUserName();
+ mDisplayName = av_name.getDisplayName();
+
+ if (mDisplayModeratorLabel)
+ {
+ mDisplayName += " " + LLTrans::getString("IM_moderator_label");
+ }
+
+ renameItem(mDisplayName);
+ if (mParent != NULL)
+ {
+ LLConversationItemSession* parent_session = dynamic_cast<LLConversationItemSession*>(mParent);
+ if (parent_session != NULL)
+ {
+ parent_session->requestSort();
+ parent_session->updateName(this);
+ postEvent("update_participant", parent_session, this);
+ }
+ }
+}
+
+void LLConversationItemParticipant::buildContextMenu(LLMenuGL& menu, U32 flags)
+{
+ menuentry_vec_t items;
+ menuentry_vec_t disabled_items;
+
+ buildParticipantMenuOptions(items, flags);
+
+ hide_context_entries(menu, items, disabled_items);
+}
+
+LLConversationItemSession* LLConversationItemParticipant::getParentSession()
+{
+ LLConversationItemSession* parent_session = NULL;
+ if (hasParent())
+ {
+ parent_session = dynamic_cast<LLConversationItemSession*>(mParent);
+ }
+ return parent_session;
+}
+
+void LLConversationItemParticipant::dumpDebugData()
+{
+ LL_INFOS() << "Merov debug : participant, uuid = " << mUUID << ", name = " << mName << ", display name = " << mDisplayName << ", muted = " << isVoiceMuted() << ", moderator = " << mIsModerator << LL_ENDL;
+}
+
+void LLConversationItemParticipant::setDisplayModeratorRole(bool displayRole)
+{
+ if (displayRole != mDisplayModeratorLabel)
+ {
+ mDisplayModeratorLabel = displayRole;
+ updateName();
+ }
+}
+
+bool LLConversationItemParticipant::isVoiceMuted()
+{
+ return mIsModeratorMuted || LLMuteList::getInstance()->isMuted(mUUID, LLMute::flagVoiceChat);
+}
+
+//
+// LLConversationSort
+//
+
+// Comparison operator: returns "true" is a comes before b, "false" otherwise
+bool LLConversationSort::operator()(const LLConversationItem* const& a, const LLConversationItem* const& b) const
+{
+ LLConversationItem::EConversationType type_a = a->getType();
+ LLConversationItem::EConversationType type_b = b->getType();
+
+ if ((type_a == LLConversationItem::CONV_PARTICIPANT) && (type_b == LLConversationItem::CONV_PARTICIPANT))
+ {
+ // If both items are participants
+ U32 sort_order = getSortOrderParticipants();
+ if (sort_order == LLConversationFilter::SO_DATE)
+ {
+ F64 time_a = 0.0;
+ F64 time_b = 0.0;
+ bool has_time_a = a->getTime(time_a);
+ bool has_time_b = b->getTime(time_b);
+ if (has_time_a && has_time_b)
+ {
+ // Most recent comes first
+ return (time_a > time_b);
+ }
+ else if (has_time_a || has_time_b)
+ {
+ // If we have only one time available, the element with time must come first
+ return has_time_a;
+ }
+ // If no time available, we'll default to sort by name at the end of this method
+ }
+ else if (sort_order == LLConversationFilter::SO_DISTANCE)
+ {
+ F64 dist_a = 0.0;
+ F64 dist_b = 0.0;
+ bool has_dist_a = a->getDistanceToAgent(dist_a);
+ bool has_dist_b = b->getDistanceToAgent(dist_b);
+ if (has_dist_a && has_dist_b)
+ {
+ // Closest comes first
+ return (dist_a < dist_b);
+ }
+ else if (has_dist_a || has_dist_b)
+ {
+ // If we have only one distance available, the element with it must come first
+ return has_dist_a;
+ }
+ // If no distance available, we'll default to sort by name at the end of this method
+ }
+ }
+ else if ((type_a > LLConversationItem::CONV_PARTICIPANT) && (type_b > LLConversationItem::CONV_PARTICIPANT))
+ {
+ // If both are sessions
+ U32 sort_order = getSortOrderSessions();
+
+ if (sort_order == LLConversationFilter::SO_DATE)
+ {
+ // Sort by time
+ F64 time_a = 0.0;
+ F64 time_b = 0.0;
+ bool has_time_a = a->getTime(time_a);
+ bool has_time_b = b->getTime(time_b);
+ if (has_time_a && has_time_b)
+ {
+ // Most recent comes first
+ return (time_a > time_b);
+ }
+ else if (has_time_a || has_time_b)
+ {
+ // If we have only one time available, the element with time must come first
+ return has_time_a;
+ }
+ // If no time available, we'll default to sort by name at the end of this method
+ }
+ else
+ {
+ if ((type_a == LLConversationItem::CONV_SESSION_NEARBY) || (type_b == LLConversationItem::CONV_SESSION_NEARBY))
+ {
+ // If one is the nearby session, put nearby session *always* last
+ return (type_b == LLConversationItem::CONV_SESSION_NEARBY);
+ }
+ else if (sort_order == LLConversationFilter::SO_SESSION_TYPE)
+ {
+ if (type_a != type_b)
+ {
+ // Lowest types come first. See LLConversationItem definition of types
+ return (type_a < type_b);
+ }
+ // If types are identical, we'll default to sort by name at the end of this method
+ }
+ }
+ }
+ else
+ {
+ // If one item is a participant and the other a session, the session comes before the participant
+ // so we simply compare the type
+ // Notes: as a consequence, CONV_UNKNOWN (which should never get created...) always come first
+ return (type_a > type_b);
+ }
+ // By default, in all other possible cases (including sort order type LLConversationFilter::SO_NAME of course),
+ // we sort by name
+ S32 compare = LLStringUtil::compareDict(a->getName(), b->getName());
+ return (compare < 0);
+}
+
+//
+// LLConversationViewModel
+//
+
+void LLConversationViewModel::sort(LLFolderViewFolder* folder)
+{
+ base_t::sort(folder);
+}
+
+// EOF
|