summaryrefslogtreecommitdiff
path: root/indra/newview
diff options
context:
space:
mode:
Diffstat (limited to 'indra/newview')
-rw-r--r--indra/newview/CMakeLists.txt2
-rw-r--r--indra/newview/llappviewer.cpp2
-rw-r--r--indra/newview/llavatarlist.cpp18
-rw-r--r--indra/newview/llavatarlist.h7
-rw-r--r--indra/newview/llavatarlistitem.cpp17
-rw-r--r--indra/newview/llavatarlistitem.h3
-rw-r--r--indra/newview/llfloaterchatmentionpicker.cpp184
-rw-r--r--indra/newview/llfloaterchatmentionpicker.h58
-rw-r--r--indra/newview/llfloaterimnearbychat.cpp2
-rw-r--r--indra/newview/llfloaterimsession.cpp2
-rw-r--r--indra/newview/llfloaterimsessiontab.cpp2
-rw-r--r--indra/newview/llpanelprofile.cpp2
-rw-r--r--indra/newview/llstartup.cpp2
-rw-r--r--indra/newview/llviewerchat.cpp8
-rw-r--r--indra/newview/llviewerfloaterreg.cpp2
-rw-r--r--indra/newview/skins/default/colors.xml6
-rw-r--r--indra/newview/skins/default/xui/en/floater_chat_mention_picker.xml31
17 files changed, 329 insertions, 19 deletions
diff --git a/indra/newview/CMakeLists.txt b/indra/newview/CMakeLists.txt
index fff1597fd9..98151e2f4d 100644
--- a/indra/newview/CMakeLists.txt
+++ b/indra/newview/CMakeLists.txt
@@ -201,6 +201,7 @@ set(viewer_SOURCE_FILES
llfloatercamera.cpp
llfloatercamerapresets.cpp
llfloaterchangeitemthumbnail.cpp
+ llfloaterchatmentionpicker.cpp
llfloaterchatvoicevolume.cpp
llfloaterclassified.cpp
llfloatercolorpicker.cpp
@@ -870,6 +871,7 @@ set(viewer_HEADER_FILES
llfloaterbuyland.h
llfloatercamerapresets.h
llfloaterchangeitemthumbnail.h
+ llfloaterchatmentionpicker.h
llfloatercamera.h
llfloaterchatvoicevolume.h
llfloaterclassified.h
diff --git a/indra/newview/llappviewer.cpp b/indra/newview/llappviewer.cpp
index edc70030b4..7e7aa521d3 100644
--- a/indra/newview/llappviewer.cpp
+++ b/indra/newview/llappviewer.cpp
@@ -475,7 +475,7 @@ static void deferred_ui_audio_callback(const LLUUID& uuid)
bool create_text_segment_icon_from_url_match(LLUrlMatch* match,LLTextBase* base)
{
- if(!match || !base || base->getPlainText())
+ if (!match || match->getSkipProfileIcon() || !base || base->getPlainText())
return false;
LLUUID match_id = match->getID();
diff --git a/indra/newview/llavatarlist.cpp b/indra/newview/llavatarlist.cpp
index 8f858fe4e1..f206474e71 100644
--- a/indra/newview/llavatarlist.cpp
+++ b/indra/newview/llavatarlist.cpp
@@ -141,6 +141,7 @@ LLAvatarList::LLAvatarList(const Params& p)
, mShowSpeakingIndicator(p.show_speaking_indicator)
, mShowPermissions(p.show_permissions_granted)
, mShowCompleteName(false)
+, mForceCompleteName(false)
{
setCommitOnSelectionChange(true);
@@ -177,7 +178,7 @@ void LLAvatarList::setShowIcons(std::string param_name)
std::string LLAvatarList::getAvatarName(LLAvatarName av_name)
{
- return mShowCompleteName? av_name.getCompleteName(false) : av_name.getDisplayName();
+ return mShowCompleteName? av_name.getCompleteName(false, mForceCompleteName) : av_name.getDisplayName();
}
// virtual
@@ -364,7 +365,7 @@ void LLAvatarList::updateAvatarNames()
for( std::vector<LLPanel*>::const_iterator it = items.begin(); it != items.end(); it++)
{
LLAvatarListItem* item = static_cast<LLAvatarListItem*>(*it);
- item->setShowCompleteName(mShowCompleteName);
+ item->setShowCompleteName(mShowCompleteName, mForceCompleteName);
item->updateAvatarName();
}
mNeedUpdateNames = false;
@@ -404,6 +405,11 @@ boost::signals2::connection LLAvatarList::setItemDoubleClickCallback(const mouse
return mItemDoubleClickSignal.connect(cb);
}
+boost::signals2::connection LLAvatarList::setItemClickedCallback(const mouse_signal_t::slot_type& cb)
+{
+ return mItemClickedSignal.connect(cb);
+}
+
//virtual
S32 LLAvatarList::notifyParent(const LLSD& info)
{
@@ -418,7 +424,7 @@ S32 LLAvatarList::notifyParent(const LLSD& info)
void LLAvatarList::addNewItem(const LLUUID& id, const std::string& name, bool is_online, EAddPosition pos)
{
LLAvatarListItem* item = new LLAvatarListItem();
- item->setShowCompleteName(mShowCompleteName);
+ item->setShowCompleteName(mShowCompleteName, mForceCompleteName);
// This sets the name as a side effect
item->setAvatarId(id, mSessionID, mIgnoreOnlineStatus);
item->setOnline(mIgnoreOnlineStatus ? true : is_online);
@@ -432,6 +438,7 @@ void LLAvatarList::addNewItem(const LLUUID& id, const std::string& name, bool is
item->setDoubleClickCallback(boost::bind(&LLAvatarList::onItemDoubleClicked, this, _1, _2, _3, _4));
+ item->setMouseDownCallback(boost::bind(&LLAvatarList::onItemClicked, this, _1, _2, _3, _4));
addItem(item, id, pos);
}
@@ -550,6 +557,11 @@ void LLAvatarList::onItemDoubleClicked(LLUICtrl* ctrl, S32 x, S32 y, MASK mask)
mItemDoubleClickSignal(ctrl, x, y, mask);
}
+void LLAvatarList::onItemClicked(LLUICtrl* ctrl, S32 x, S32 y, MASK mask)
+{
+ mItemClickedSignal(ctrl, x, y, mask);
+}
+
bool LLAvatarItemComparator::compare(const LLPanel* item1, const LLPanel* item2) const
{
const LLAvatarListItem* avatar_item1 = dynamic_cast<const LLAvatarListItem*>(item1);
diff --git a/indra/newview/llavatarlist.h b/indra/newview/llavatarlist.h
index af5bfefcde..f99da93a3d 100644
--- a/indra/newview/llavatarlist.h
+++ b/indra/newview/llavatarlist.h
@@ -96,11 +96,13 @@ public:
boost::signals2::connection setItemDoubleClickCallback(const mouse_signal_t::slot_type& cb);
+ boost::signals2::connection setItemClickedCallback(const mouse_signal_t::slot_type& cb);
+
virtual S32 notifyParent(const LLSD& info);
void handleDisplayNamesOptionChanged();
- void setShowCompleteName(bool show) { mShowCompleteName = show;};
+ void setShowCompleteName(bool show, bool force = false) { mShowCompleteName = show; mForceCompleteName = force; };
protected:
void refresh();
@@ -113,6 +115,7 @@ protected:
void updateLastInteractionTimes();
void rebuildNames();
void onItemDoubleClicked(LLUICtrl* ctrl, S32 x, S32 y, MASK mask);
+ void onItemClicked(LLUICtrl* ctrl, S32 x, S32 y, MASK mask);
void updateAvatarNames();
private:
@@ -127,6 +130,7 @@ private:
bool mShowSpeakingIndicator;
bool mShowPermissions;
bool mShowCompleteName;
+ bool mForceCompleteName;
LLTimer* mLITUpdateTimer; // last interaction time update timer
std::string mIconParamName;
@@ -138,6 +142,7 @@ private:
commit_signal_t mRefreshCompleteSignal;
mouse_signal_t mItemDoubleClickSignal;
+ mouse_signal_t mItemClickedSignal;
};
/** Abstract comparator for avatar items */
diff --git a/indra/newview/llavatarlistitem.cpp b/indra/newview/llavatarlistitem.cpp
index 880910d18e..6ef45ed160 100644
--- a/indra/newview/llavatarlistitem.cpp
+++ b/indra/newview/llavatarlistitem.cpp
@@ -78,6 +78,7 @@ LLAvatarListItem::LLAvatarListItem(bool not_from_ui_factory/* = true*/)
mShowProfileBtn(true),
mShowPermissions(false),
mShowCompleteName(false),
+ mForceCompleteName(false),
mHovered(false),
mAvatarNameCacheConnection(),
mGreyOutUsername("")
@@ -324,13 +325,11 @@ void LLAvatarListItem::setShowProfileBtn(bool show)
void LLAvatarListItem::showSpeakingIndicator(bool visible)
{
- // Already done? Then do nothing.
- if (mSpeakingIndicator->getVisible() == (bool)visible)
- return;
-// Disabled to not contradict with SpeakingIndicatorManager functionality. EXT-3976
-// probably this method should be totally removed.
-// mSpeakingIndicator->setVisible(visible);
-// updateChildren();
+ if (mSpeakingIndicator)
+ {
+ mSpeakingIndicator->setIsActiveChannel(visible);
+ mSpeakingIndicator->setShowParticipantsSpeaking(visible);
+ }
}
void LLAvatarListItem::setAvatarIconVisible(bool visible)
@@ -417,8 +416,8 @@ void LLAvatarListItem::onAvatarNameCache(const LLAvatarName& av_name)
mAvatarNameCacheConnection.disconnect();
mGreyOutUsername = "";
- std::string name_string = mShowCompleteName? av_name.getCompleteName(false) : av_name.getDisplayName();
- if(av_name.getCompleteName() != av_name.getUserName())
+ std::string name_string = mShowCompleteName? av_name.getCompleteName(false, mForceCompleteName) : av_name.getDisplayName();
+ if(av_name.getCompleteName(false, mForceCompleteName) != av_name.getUserName())
{
mGreyOutUsername = "[ " + av_name.getUserName(true) + " ]";
LLStringUtil::toLower(mGreyOutUsername);
diff --git a/indra/newview/llavatarlistitem.h b/indra/newview/llavatarlistitem.h
index 2e4c597d30..2ec7a41055 100644
--- a/indra/newview/llavatarlistitem.h
+++ b/indra/newview/llavatarlistitem.h
@@ -106,7 +106,7 @@ public:
void setShowPermissions(bool show) { mShowPermissions = show; };
void showLastInteractionTime(bool show);
void setAvatarIconVisible(bool visible);
- void setShowCompleteName(bool show) { mShowCompleteName = show;};
+ void setShowCompleteName(bool show, bool force = false) { mShowCompleteName = show; mForceCompleteName = force;};
const LLUUID& getAvatarId() const;
std::string getAvatarName() const;
@@ -220,6 +220,7 @@ private:
bool mHovered;
bool mShowCompleteName;
+ bool mForceCompleteName;
std::string mGreyOutUsername;
void fetchAvatarName();
diff --git a/indra/newview/llfloaterchatmentionpicker.cpp b/indra/newview/llfloaterchatmentionpicker.cpp
new file mode 100644
index 0000000000..1cfed122a9
--- /dev/null
+++ b/indra/newview/llfloaterchatmentionpicker.cpp
@@ -0,0 +1,184 @@
+/**
+ * @file llfloaterchatmentionpicker.cpp
+ *
+ * $LicenseInfo:firstyear=2025&license=viewerlgpl$
+ * Second Life Viewer Source Code
+ * Copyright (C) 2025, 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 "llfloaterchatmentionpicker.h"
+
+#include "llavatarlist.h"
+#include "llfloaterimcontainer.h"
+#include "llchatmentionhelper.h"
+#include "llparticipantlist.h"
+
+LLUUID LLFloaterChatMentionPicker::sSessionID(LLUUID::null);
+
+LLFloaterChatMentionPicker::LLFloaterChatMentionPicker(const LLSD& key)
+: LLFloater(key), mAvatarList(NULL)
+{
+ // This floater should hover on top of our dependent (with the dependent having the focus)
+ setFocusStealsFrontmost(false);
+ setBackgroundVisible(false);
+ setAutoFocus(false);
+}
+
+bool LLFloaterChatMentionPicker::postBuild()
+{
+ mAvatarList = getChild<LLAvatarList>("avatar_list");
+ mAvatarList->setShowCompleteName(true, true);
+ mAvatarList->setFocusOnItemClicked(false);
+ mAvatarList->setItemClickedCallback([this](LLUICtrl* ctrl, S32 x, S32 y, MASK mask)
+ {
+ if (LLAvatarListItem* item = dynamic_cast<LLAvatarListItem*>(ctrl))
+ {
+ selectResident(item->getAvatarId());
+ }
+ });
+ mAvatarList->setRefreshCompleteCallback([this](LLUICtrl* ctrl, const LLSD& param)
+ {
+ if (mAvatarList->numSelected() == 0)
+ {
+ mAvatarList->selectFirstItem();
+ }
+ });
+
+ return LLFloater::postBuild();
+}
+
+void LLFloaterChatMentionPicker::onOpen(const LLSD& key)
+{
+ buildAvatarList();
+ mAvatarList->setNameFilter(key.has("av_name") ? key["av_name"].asString() : "");
+
+ gFloaterView->adjustToFitScreen(this, false);
+}
+
+uuid_vec_t LLFloaterChatMentionPicker::getParticipantIds()
+{
+ LLParticipantList* item = dynamic_cast<LLParticipantList*>(LLFloaterIMContainer::getInstance()->getSessionModel(sSessionID));
+ if (!item)
+ {
+ LL_WARNS() << "Participant list is missing" << LL_ENDL;
+ return {};
+ }
+
+ uuid_vec_t avatar_ids;
+ LLFolderViewModelItemCommon::child_list_t::const_iterator current_participant_model = item->getChildrenBegin();
+ LLFolderViewModelItemCommon::child_list_t::const_iterator end_participant_model = item->getChildrenEnd();
+ while (current_participant_model != end_participant_model)
+ {
+ LLConversationItem* participant_model = dynamic_cast<LLConversationItem*>(*current_participant_model);
+ if (participant_model)
+ {
+ avatar_ids.push_back(participant_model->getUUID());
+ }
+ current_participant_model++;
+ }
+ return avatar_ids;
+}
+
+void LLFloaterChatMentionPicker::buildAvatarList()
+{
+ uuid_vec_t& avatar_ids = mAvatarList->getIDs();
+ avatar_ids = getParticipantIds();
+ updateAvatarList(avatar_ids);
+ mAvatarList->setDirty();
+}
+
+void LLFloaterChatMentionPicker::selectResident(const LLUUID& id)
+{
+ if (id.isNull())
+ return;
+
+ setValue(stringize("secondlife:///app/agent/", id.asString(), "/mention "));
+ onCommit();
+ LLChatMentionHelper::instance().hideHelper();
+}
+
+void LLFloaterChatMentionPicker::onClose(bool app_quitting)
+{
+ if (!app_quitting)
+ {
+ LLChatMentionHelper::instance().hideHelper();
+ }
+}
+
+bool LLFloaterChatMentionPicker::handleKey(KEY key, MASK mask, bool called_from_parent)
+{
+ if (mask == MASK_NONE)
+ {
+ switch (key)
+ {
+ case KEY_UP:
+ case KEY_DOWN:
+ return mAvatarList->handleKey(key, mask, called_from_parent);
+ case KEY_RETURN:
+ case KEY_TAB:
+ selectResident(mAvatarList->getSelectedUUID());
+ return true;
+ case KEY_ESCAPE:
+ LLChatMentionHelper::instance().hideHelper();
+ return true;
+ case KEY_LEFT:
+ case KEY_RIGHT:
+ return true;
+ default:
+ break;
+ }
+ }
+ return LLFloater::handleKey(key, mask, called_from_parent);
+}
+
+void LLFloaterChatMentionPicker::goneFromFront()
+{
+ LLChatMentionHelper::instance().hideHelper();
+}
+
+void LLFloaterChatMentionPicker::updateSessionID(LLUUID session_id)
+{
+ sSessionID = session_id;
+
+ LLParticipantList* item = dynamic_cast<LLParticipantList*>(LLFloaterIMContainer::getInstance()->getSessionModel(sSessionID));
+ if (!item)
+ {
+ LL_WARNS() << "Participant list is missing" << LL_ENDL;
+ return;
+ }
+
+ uuid_vec_t avatar_ids = getParticipantIds();
+ updateAvatarList(avatar_ids);
+}
+
+void LLFloaterChatMentionPicker::updateAvatarList(uuid_vec_t& avatar_ids)
+{
+ std::vector<std::string> av_names;
+ for (auto& id : avatar_ids)
+ {
+ LLAvatarName av_name;
+ LLAvatarNameCache::get(id, &av_name);
+ av_names.push_back(utf8str_tolower(av_name.getAccountName()));
+ av_names.push_back(utf8str_tolower(av_name.getDisplayName()));
+ }
+ LLChatMentionHelper::instance().updateAvatarList(av_names);
+}
diff --git a/indra/newview/llfloaterchatmentionpicker.h b/indra/newview/llfloaterchatmentionpicker.h
new file mode 100644
index 0000000000..8d221d7a89
--- /dev/null
+++ b/indra/newview/llfloaterchatmentionpicker.h
@@ -0,0 +1,58 @@
+/**
+ * @file llfloaterchatmentionpicker.h
+ *
+ * $LicenseInfo:firstyear=2025&license=viewerlgpl$
+ * Second Life Viewer Source Code
+ * Copyright (C) 2025, 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$
+ */
+
+#ifndef LLFLOATERCHATMENTIONPICKER_H
+#define LLFLOATERCHATMENTIONPICKER_H
+
+#include "llfloater.h"
+
+class LLAvatarList;
+
+class LLFloaterChatMentionPicker : public LLFloater
+{
+public:
+ LLFloaterChatMentionPicker(const LLSD& key);
+
+ virtual bool postBuild() override;
+ virtual void goneFromFront() override;
+
+ void buildAvatarList();
+
+ static uuid_vec_t getParticipantIds();
+ static void updateSessionID(LLUUID session_id);
+ static void updateAvatarList(uuid_vec_t& avatar_ids);
+
+private:
+
+ void onOpen(const LLSD& key) override;
+ void onClose(bool app_quitting) override;
+ virtual bool handleKey(KEY key, MASK mask, bool called_from_parent) override;
+ void selectResident(const LLUUID& id);
+
+ static LLUUID sSessionID;
+ LLAvatarList* mAvatarList;
+};
+
+#endif
diff --git a/indra/newview/llfloaterimnearbychat.cpp b/indra/newview/llfloaterimnearbychat.cpp
index db6f9ac22a..b649514bff 100644
--- a/indra/newview/llfloaterimnearbychat.cpp
+++ b/indra/newview/llfloaterimnearbychat.cpp
@@ -586,7 +586,7 @@ void LLFloaterIMNearbyChat::sendChat( EChatType type )
{
if (mInputEditor)
{
- LLWString text = mInputEditor->getWText();
+ LLWString text = mInputEditor->getConvertedText();
LLWStringUtil::trim(text);
LLWStringUtil::replaceChar(text,182,'\n'); // Convert paragraph symbols back into newlines.
if (!text.empty())
diff --git a/indra/newview/llfloaterimsession.cpp b/indra/newview/llfloaterimsession.cpp
index 185274981b..84a9fad708 100644
--- a/indra/newview/llfloaterimsession.cpp
+++ b/indra/newview/llfloaterimsession.cpp
@@ -251,7 +251,7 @@ void LLFloaterIMSession::sendMsgFromInputEditor()
{
if (mInputEditor)
{
- LLWString text = mInputEditor->getWText();
+ LLWString text = mInputEditor->getConvertedText();
LLWStringUtil::trim(text);
LLWStringUtil::replaceChar(text,182,'\n'); // Convert paragraph symbols back into newlines.
if(!text.empty())
diff --git a/indra/newview/llfloaterimsessiontab.cpp b/indra/newview/llfloaterimsessiontab.cpp
index 50e765c236..96aac8c1e6 100644
--- a/indra/newview/llfloaterimsessiontab.cpp
+++ b/indra/newview/llfloaterimsessiontab.cpp
@@ -35,6 +35,7 @@
#include "llavatariconctrl.h"
#include "llchatentry.h"
#include "llchathistory.h"
+#include "llfloaterchatmentionpicker.h"
#include "llchiclet.h"
#include "llchicletbar.h"
#include "lldraghandle.h"
@@ -485,6 +486,7 @@ void LLFloaterIMSessionTab::onFocusReceived()
LLIMModel::instance().sendNoUnreadMessages(mSessionID);
}
+ LLFloaterChatMentionPicker::updateSessionID(mSessionID);
super::onFocusReceived();
}
diff --git a/indra/newview/llpanelprofile.cpp b/indra/newview/llpanelprofile.cpp
index 08605f7cf4..c81746a48a 100644
--- a/indra/newview/llpanelprofile.cpp
+++ b/indra/newview/llpanelprofile.cpp
@@ -328,7 +328,7 @@ public:
}
const std::string verb = params[1].asString();
- if (verb == "about")
+ if (verb == "about" || verb == "mention")
{
LLAvatarActions::showProfile(avatar_id);
return true;
diff --git a/indra/newview/llstartup.cpp b/indra/newview/llstartup.cpp
index 3973036cc6..4f60f98b49 100644
--- a/indra/newview/llstartup.cpp
+++ b/indra/newview/llstartup.cpp
@@ -3566,7 +3566,7 @@ bool process_login_success_response()
// Agent id needed for parcel info request in LLUrlEntryParcel
// to resolve parcel name.
- LLUrlEntryParcel::setAgentID(gAgentID);
+ LLUrlEntryBase::setAgentID(gAgentID);
text = response["session_id"].asString();
if(!text.empty()) gAgentSessionID.set(text);
diff --git a/indra/newview/llviewerchat.cpp b/indra/newview/llviewerchat.cpp
index 8b01c4ef88..2ca2c5c07d 100644
--- a/indra/newview/llviewerchat.cpp
+++ b/indra/newview/llviewerchat.cpp
@@ -36,6 +36,7 @@
#include "llviewerregion.h"
#include "llworld.h"
#include "llinstantmessage.h" //SYSTEM_FROM
+#include "llurlregistry.h"
// LLViewerChat
LLViewerChat::font_change_signal_t LLViewerChat::sChatFontChangedSignal;
@@ -222,6 +223,13 @@ void LLViewerChat::formatChatMsg(const LLChat& chat, std::string& formated_msg)
{
std::string tmpmsg = chat.mText;
+ // show @name instead of slurl for chat mentions
+ LLUrlMatch match;
+ while (LLUrlRegistry::instance().findUrl(tmpmsg, match, LLUrlRegistryNullCallback, false, true))
+ {
+ tmpmsg.replace(match.getStart(), match.getEnd() - match.getStart() + 1, match.getLabel());
+ }
+
if(chat.mChatStyle == CHAT_STYLE_IRC)
{
formated_msg = chat.mFromName + tmpmsg.substr(3);
diff --git a/indra/newview/llviewerfloaterreg.cpp b/indra/newview/llviewerfloaterreg.cpp
index 95c2a77ba8..4d9c2f3281 100644
--- a/indra/newview/llviewerfloaterreg.cpp
+++ b/indra/newview/llviewerfloaterreg.cpp
@@ -58,6 +58,7 @@
#include "llfloatercamera.h"
#include "llfloatercamerapresets.h"
#include "llfloaterchangeitemthumbnail.h"
+#include "llfloaterchatmentionpicker.h"
#include "llfloaterchatvoicevolume.h"
#include "llfloaterclassified.h"
#include "llfloaterconversationlog.h"
@@ -353,6 +354,7 @@ void LLViewerFloaterReg::registerFloaters()
LLFloaterReg::add("chat_voice", "floater_voice_chat_volume.xml", (LLFloaterBuildFunc)&LLFloaterReg::build<LLFloaterChatVoiceVolume>);
LLFloaterReg::add("change_item_thumbnail", "floater_change_item_thumbnail.xml", (LLFloaterBuildFunc)&LLFloaterReg::build<LLFloaterChangeItemThumbnail>);
LLFloaterReg::add("nearby_chat", "floater_im_session.xml", (LLFloaterBuildFunc)&LLFloaterIMNearbyChat::buildFloater);
+ LLFloaterReg::add("chat_mention_picker", "floater_chat_mention_picker.xml", (LLFloaterBuildFunc)&LLFloaterReg::build<LLFloaterChatMentionPicker>);
LLFloaterReg::add("classified", "floater_classified.xml", (LLFloaterBuildFunc)&LLFloaterReg::build<LLFloaterClassified>);
LLFloaterReg::add("compile_queue", "floater_script_queue.xml", (LLFloaterBuildFunc)&LLFloaterReg::build<LLFloaterCompileQueue>);
LLFloaterReg::add("conversation", "floater_conversation_log.xml", (LLFloaterBuildFunc)&LLFloaterReg::build<LLFloaterConversationLog>);
diff --git a/indra/newview/skins/default/colors.xml b/indra/newview/skins/default/colors.xml
index f0af4acf20..cb190d3d34 100644
--- a/indra/newview/skins/default/colors.xml
+++ b/indra/newview/skins/default/colors.xml
@@ -1000,4 +1000,10 @@
<color
name="OutfitSnapshotMacMask2"
value="0.1 0.1 0.1 1"/>
+ <color
+ name="ChatMentionHighlight"
+ value="0.82 0.91 0.98 0.15" />
+ <color
+ name="ChatSelfMentionHighlight"
+ value="0.85 0.75 0.40 0.5" />
</colors>
diff --git a/indra/newview/skins/default/xui/en/floater_chat_mention_picker.xml b/indra/newview/skins/default/xui/en/floater_chat_mention_picker.xml
new file mode 100644
index 0000000000..bbad99f932
--- /dev/null
+++ b/indra/newview/skins/default/xui/en/floater_chat_mention_picker.xml
@@ -0,0 +1,31 @@
+<?xml version="1.0" encoding="utf-8" standalone="yes" ?>
+<floater
+ name="chat_mention_picker"
+ title="CHOOSE RESIDENT"
+ single_instance="true"
+ can_minimize="false"
+ can_tear_off="false"
+ can_resize="true"
+ auto_close="true"
+ layout="topleft"
+ min_width="250"
+ chrome="true"
+ height="125"
+ width="310">
+ <avatar_list
+ allow_select="true"
+ follows="all"
+ height="120"
+ width="306"
+ ignore_online_status="true"
+ layout="topleft"
+ left="3"
+ keep_one_selected="true"
+ multi_select="false"
+ show_info_btn="false"
+ show_profile_btn="false"
+ show_speaking_indicator="false"
+ name="avatar_list"
+ right="-1"
+ top="2" />
+</floater>