diff --git a/indra/newview/app_settings/settings.xml b/indra/newview/app_settings/settings.xml index bf69cac69c..1a3c052835 100644 --- a/indra/newview/app_settings/settings.xml +++ b/indra/newview/app_settings/settings.xml @@ -18755,7 +18755,7 @@ Change of this parameter will affect the layout of buttons in notification toast FSChatbarGestureAutoCompleteEnable Comment - Toggles the auto complete in chat bar + Toggles gesture auto complete in chat bar Persist 1 Type @@ -18763,6 +18763,17 @@ Change of this parameter will affect the layout of buttons in notification toast Value 1 + FSChatBarNamePrediction + + Comment + Toggles name prediction in nearby chat + Persist + 1 + Type + Boolean + Value + 0 + FSDisableAvatarTrackerAtCloseIn Comment diff --git a/indra/newview/fsnearbychatcontrol.cpp b/indra/newview/fsnearbychatcontrol.cpp index ad6543d651..d53b5ab436 100644 --- a/indra/newview/fsnearbychatcontrol.cpp +++ b/indra/newview/fsnearbychatcontrol.cpp @@ -88,6 +88,11 @@ // #include "llanimationstates.h" // ANIM_AGENT_WHISPER, ANIM_AGENT_TALK, ANIM_AGENT_SHOUT // #include "llviewerstats.h" // +// FIRE-3192 - Name Prediction +#include "llworld.h" + +#define NAME_PREDICTION_MINIMUM_LENGTH 2 +// static LLDefaultChildRegistry::Register r("fs_nearby_chat_control"); @@ -199,6 +204,114 @@ void FSNearbyChatControl::onKeystroke(LLLineEditor* caller,void* userdata) caller->setCursorToEnd(); } } +// FIRE-3192 - Predictive name completion, based on code by Satomi Ahn + static LLCachedControl sNameAutocomplete(gSavedSettings, "FSChatBarNamePrediction"); + if (length > NAME_PREDICTION_MINIMUM_LENGTH && sNameAutocomplete && key < KEY_SPECIAL) + { + std::string text = caller->getText(); + S32 cur_pos = caller->getCursor(); + if (cur_pos && (text.at(cur_pos - 1) != ' ' || caller->hasSelection())) + { + // Get a list of avatars within range + std::vector avatar_ids; + std::vector positions; + LLWorld::getInstance()->getAvatars(&avatar_ids, &positions, gAgent.getPositionGlobal(), gSavedSettings.getF32("NearMeRange")); + + if (avatar_ids.empty()) return; // Nobody's in range! + + // Parse text for a pattern to search + std::string prefix = text.substr(0, cur_pos); // Text before search string + std::string suffix = text.substr(cur_pos, text.length() - cur_pos); // Text after search string + U32 last_space = prefix.rfind(" "); + std::string pattern = prefix.substr(last_space + 1, prefix.length() - last_space - 1); // Search pattern + + prefix = prefix.substr(0, last_space + 1); + std::string match_pattern = ""; + + if (pattern.size() < NAME_PREDICTION_MINIMUM_LENGTH) return; + + match_pattern = prefix.substr(last_space + 1, prefix.length() - last_space -1); + prefix = prefix.substr(0, last_space + 1); + LLStringUtil::toLower(pattern); + + std::string name; + bool found = false; + bool full_name = false; + std::vector::iterator iter = avatar_ids.begin(); + + if (last_space != std::string::npos && !prefix.empty()) + { + last_space = prefix.substr(0, prefix.length() - 2).rfind(" "); + match_pattern = prefix.substr(last_space + 1, prefix.length() - last_space -1); + prefix = prefix.substr(0, last_space + 1); + // prepare search pattern + std::string full_pattern(match_pattern + pattern); + LLStringUtil::toLower(full_pattern); + // Look for a match + while (iter != avatar_ids.end() && !found) + { + if ((bool)gCacheName->getFullName(*iter++, name)) + { + if (gRlvHandler.hasBehaviour(RLV_BHVR_SHOWNAMES)) + { + name = RlvStrings::getAnonym(name); + } + LLStringUtil::toLower(name); + found = (name.find(full_pattern) == 0); + } + } + } + + if (found) + { + full_name = true; // ignore OnlyFirstName in case we want to disambiguate + } + else if (!pattern.empty()) // if first search did not work, try matching with last word before cursor only + { + prefix += match_pattern; // first part of the pattern wasn't a pattern, so keep it in prefix + LLStringUtil::toLower(pattern); + iter = avatar_ids.begin(); + // Look for a match + while (iter != avatar_ids.end() && !found) + { + if ((bool)gCacheName->getFullName(*iter++, name)) + { + if (gRlvHandler.hasBehaviour(RLV_BHVR_SHOWNAMES)) + { + name = RlvStrings::getAnonym(name); + } + LLStringUtil::toLower(name); + found = (name.find(pattern) == 0); + } + } + } + + // if we found something by either method, replace the pattern by the avatar name + if (found) + { + std::string name; + gCacheName->getFullName(*(iter - 1), name); + if (gRlvHandler.hasBehaviour(RLV_BHVR_SHOWNAMES)) + { + prefix += RlvStrings::getAnonym(name) + " "; + } + else + { + if (!full_name) + { + U32 space = name.find(" "); + if (space != std::string::npos) + name = name.substr(0, space); + } + + prefix += name + " "; + } + caller->setText(prefix + suffix); + caller->setSelection(prefix.length(), cur_pos); + } + } + } +// } BOOL FSNearbyChatControl::matchChatTypeTrigger(const std::string& in_str, std::string* out_str)