diff options
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
  | 
