From 4b02740f094d1f03a6276eee497a69da5d3cd9dd Mon Sep 17 00:00:00 2001 From: Ansariel Date: Wed, 11 Nov 2015 17:37:33 +0100 Subject: [PATCH] Maintenance: Get rid of code duplication in FSFloaterNearbyChat::onChatBoxKeystroke() and FSNearbyChatControl::onKeystroke() --- indra/newview/fsfloaternearbychat.cpp | 215 +------------------ indra/newview/fsfloaternearbychat.h | 1 - indra/newview/fsnearbychatcontrol.cpp | 287 +++----------------------- indra/newview/fsnearbychatcontrol.h | 6 +- indra/newview/fsnearbychathub.cpp | 275 ++++++++++++++++++++++++ indra/newview/fsnearbychathub.h | 9 +- 6 files changed, 312 insertions(+), 481 deletions(-) diff --git a/indra/newview/fsfloaternearbychat.cpp b/indra/newview/fsfloaternearbychat.cpp index 841f5bd30b..f729ab46e9 100644 --- a/indra/newview/fsfloaternearbychat.cpp +++ b/indra/newview/fsfloaternearbychat.cpp @@ -74,7 +74,6 @@ #include "llworld.h" #include "rlvhandler.h" -static const U32 NAME_PREDICTION_MINIMUM_LENGTH = 3; S32 FSFloaterNearbyChat::sLastSpecialChatChannel = 0; // [RLVa:KB] - Checked: 2010-02-27 (RLVa-0.2.2) @@ -725,221 +724,9 @@ BOOL FSFloaterNearbyChat::handleKeyHere( KEY key, MASK mask ) return handled; } -BOOL FSFloaterNearbyChat::matchChatTypeTrigger(const std::string& in_str, std::string* out_str) -{ - U32 in_len = in_str.length(); - S32 cnt = sizeof(sChatTypeTriggers) / sizeof(*sChatTypeTriggers); - - bool string_was_found = false; - - for (S32 n = 0; n < cnt && !string_was_found; n++) - { - if (in_len <= sChatTypeTriggers[n].name.length()) - { - std::string trigger_trunc = sChatTypeTriggers[n].name; - LLStringUtil::truncate(trigger_trunc, in_len); - - if (!LLStringUtil::compareInsensitive(in_str, trigger_trunc)) - { - *out_str = sChatTypeTriggers[n].name; - string_was_found = true; - } - } - } - - return string_was_found; -} - void FSFloaterNearbyChat::onChatBoxKeystroke() { - LLWString raw_text = mInputEditor->getWText(); - - // Can't trim the end, because that will cause autocompletion - // to eat trailing spaces that might be part of a gesture. - LLWStringUtil::trimHead(raw_text); - S32 length = raw_text.length(); - - S32 channel=0; - if (gSavedSettings.getBOOL("FSNearbyChatbar") && - gSavedSettings.getBOOL("FSShowChatChannel")) - { - // [FS communication UI] - //channel = (S32)(LLFloaterNearbyChat::getInstance()->getChild("ChatChannel")->get()); - channel = (S32)(FSFloaterNearbyChat::getInstance()->getChild("ChatChannel")->get()); - // [FS communication UI] - } - - // if( (length > 0) && (raw_text[0] != '/') ) // forward slash is used for escape (eg. emote) sequences - // [RLVa:KB] - Checked: 2010-03-26 (RLVa-1.2.0b) | Modified: RLVa-1.0.0d - if (length > 0 && - raw_text[0] != '/' && (raw_text[0] != ':' || !gSavedSettings.getBOOL("AllowMUpose")) && - !gRlvHandler.hasBehaviour(RLV_BHVR_REDIRCHAT)) - // [/RLVa:KB] - { - // only start typing animation if we are chatting without / on channel 0 -Zi - if(channel==0) - gAgent.startTyping(); - } - else - { - gAgent.stopTyping(); - } - - KEY key = gKeyboard->currentKey(); - MASK mask = gKeyboard->currentMask(FALSE); - - // Ignore "special" keys, like backspace, arrows, etc. - if (length > 1 - && raw_text[0] == '/' - && key < KEY_SPECIAL - && gSavedSettings.getBOOL("FSChatbarGestureAutoCompleteEnable")) - { - // we're starting a gesture, attempt to autocomplete - - std::string utf8_trigger = wstring_to_utf8str(raw_text); - std::string utf8_out_str(utf8_trigger); - - if (LLGestureMgr::instance().matchPrefix(utf8_trigger, &utf8_out_str)) - { - std::string rest_of_match = utf8_out_str.substr(utf8_trigger.size()); - if (!rest_of_match.empty()) - { - mInputEditor->setText(utf8_trigger + rest_of_match); // keep original capitalization for user-entered part - - // Select to end of line, starting from the character - // after the last one the user typed. - mInputEditor->selectByCursorPosition(utf8_out_str.size()-rest_of_match.size(),utf8_out_str.size()); - } - } - else if (matchChatTypeTrigger(utf8_trigger, &utf8_out_str)) - { - std::string rest_of_match = utf8_out_str.substr(utf8_trigger.size()); - mInputEditor->setText(utf8_trigger + rest_of_match + " "); // keep original capitalization for user-entered part - mInputEditor->endOfDoc(); - } - } - - // 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 && mask != MASK_CONTROL) - { - S32 cur_pos = mInputEditor->getCursorPos(); - if (cur_pos && (raw_text[cur_pos - 1] != ' ')) - { - // Get a list of avatars within range - uuid_vec_t avatar_ids; - LLWorld::getInstance()->getAvatars(&avatar_ids, NULL, gAgent.getPositionGlobal(), gSavedSettings.getF32("NearMeRange")); - - if (avatar_ids.empty()) return; // Nobody's in range! - - // Parse text for a pattern to search - std::string prefix = wstring_to_utf8str(raw_text.substr(0, cur_pos)); // Text before search string - std::string suffix = ""; - if (cur_pos <= raw_text.length()) // Is there anything after the cursor? - { - suffix = wstring_to_utf8str(raw_text.substr(cur_pos)); // Text after search string - } - size_t 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); - std::string match = pattern; - LLStringUtil::toLower(pattern); - - std::string name; - bool found = false; - bool full_name = false; - uuid_vec_t::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 (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 - prefix += match_pattern; - } - 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 (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 first_name, last_name; - gCacheName->getFirstLastName(*(iter - 1), first_name, last_name); - std::string rest_of_match; - std::string replaced_text; - if (gRlvHandler.hasBehaviour(RLV_BHVR_SHOWNAMES)) - { - replaced_text += RlvStrings::getAnonym(first_name + " " + last_name) + " "; - } - else - { - if (full_name) - { - rest_of_match = /*first_name + " " +*/ last_name.substr(pattern.size()); - } - else - { - rest_of_match = first_name.substr(pattern.size()); - } - replaced_text += match + rest_of_match + " "; - } - if (!rest_of_match.empty()) - { - mInputEditor->setText(prefix + replaced_text + suffix); - mInputEditor->selectByCursorPosition(utf8string_to_wstring(prefix).size() + utf8string_to_wstring(match).size(), utf8string_to_wstring(prefix).size() + utf8string_to_wstring(replaced_text).size()); - } - } - } - } - // + FSNearbyChat::handleChatBarKeystroke(mInputEditor); } // static diff --git a/indra/newview/fsfloaternearbychat.h b/indra/newview/fsfloaternearbychat.h index 2f4c51eb30..0b66c0b29d 100644 --- a/indra/newview/fsfloaternearbychat.h +++ b/indra/newview/fsfloaternearbychat.h @@ -103,7 +103,6 @@ public: void updateShowMutedChatHistory(const LLSD &data); protected: - static BOOL matchChatTypeTrigger(const std::string& in_str, std::string* out_str); void onChatBoxKeystroke(); void onChatBoxFocusLost(); void onChatBoxFocusReceived(); diff --git a/indra/newview/fsnearbychatcontrol.cpp b/indra/newview/fsnearbychatcontrol.cpp index 48a279b497..26ba7f6038 100644 --- a/indra/newview/fsnearbychatcontrol.cpp +++ b/indra/newview/fsnearbychatcontrol.cpp @@ -37,44 +37,26 @@ #include "fsfloaternearbychat.h" #include "llagent.h" // gAgent #include "llagentcamera.h" // gAgentCamera +#include "llautoreplace.h" #include "llfloaterimnearbychathandler.h" -//AO - includes for textentry -#include "llautoreplace.h" -#include "llgesturemgr.h" -#include "llkeyboard.h" -#include "llworld.h" // FIRE-3192 - Name Prediction -#include "rlvhandler.h" - -static const U32 NAME_PREDICTION_MINIMUM_LENGTH = 3; - static LLDefaultChildRegistry::Register r("fs_nearby_chat_control"); -struct LLChatTypeTrigger { - std::string name; - EChatType type; -}; - -static LLChatTypeTrigger sChatTypeTriggers[] = { - { "/whisper" , CHAT_TYPE_WHISPER}, - { "/shout" , CHAT_TYPE_SHOUT} -}; - FSNearbyChatControl::FSNearbyChatControl(const FSNearbyChatControl::Params& p) : LLLineEditor(p) { // FIRE-11373: Autoreplace doesn't work in nearby chat bar setAutoreplaceCallback(boost::bind(&LLAutoReplace::autoreplaceCallback, LLAutoReplace::getInstance(), _1, _2, _3, _4, _5)); - setKeystrokeCallback(onKeystroke,this); + setKeystrokeCallback(onKeystroke, this); FSNearbyChat::instance().registerChatBar(this); setEnableLineHistory(TRUE); - setIgnoreArrowKeys( FALSE ); - setCommitOnFocusLost( FALSE ); - setRevertOnEsc( FALSE ); - setIgnoreTab( TRUE ); - setReplaceNewlinesWithSpaces( FALSE ); - setPassDelete( TRUE ); + setIgnoreArrowKeys(FALSE); + setCommitOnFocusLost(FALSE); + setRevertOnEsc(FALSE); + setIgnoreTab(TRUE); + setReplaceNewlinesWithSpaces(FALSE); + setPassDelete(TRUE); setFont(LLViewerChat::getChatFont()); // Register for font change notifications @@ -85,252 +67,40 @@ FSNearbyChatControl::~FSNearbyChatControl() { } -void FSNearbyChatControl::onKeystroke(LLLineEditor* caller,void* userdata) +void FSNearbyChatControl::onKeystroke(LLLineEditor* caller, void* userdata) { - LLWString raw_text = caller->getWText(); - - // Can't trim the end, because that will cause autocompletion - // to eat trailing spaces that might be part of a gesture. - LLWStringUtil::trimHead(raw_text); - S32 length = raw_text.length(); - - S32 channel=0; - if (gSavedSettings.getBOOL("FSNearbyChatbar") && - gSavedSettings.getBOOL("FSShowChatChannel")) - { - // [FS communication UI] - //channel = (S32)(LLFloaterNearbyChat::getInstance()->getChild("ChatChannel")->get()); - channel = (S32)(FSFloaterNearbyChat::getInstance()->getChild("ChatChannel")->get()); - // [FS communication UI] - } - - // if( (length > 0) && (raw_text[0] != '/') ) // forward slash is used for escape (eg. emote) sequences - // [RLVa:KB] - Checked: 2010-03-26 (RLVa-1.2.0b) | Modified: RLVa-1.0.0d - if (length > 0 && - raw_text[0] != '/' && (raw_text[0] != ':' || !gSavedSettings.getBOOL("AllowMUpose")) && - !gRlvHandler.hasBehaviour(RLV_BHVR_REDIRCHAT)) - // [/RLVa:KB] - { - // only start typing animation if we are chatting without / on channel 0 -Zi - if(channel==0) - gAgent.startTyping(); - } - else - { - gAgent.stopTyping(); - } - - KEY key = gKeyboard->currentKey(); - MASK mask = gKeyboard->currentMask(FALSE); - - // Ignore "special" keys, like backspace, arrows, etc. - if (length > 1 - && raw_text[0] == '/' - && key < KEY_SPECIAL - && gSavedSettings.getBOOL("FSChatbarGestureAutoCompleteEnable")) - { - // we're starting a gesture, attempt to autocomplete - - std::string utf8_trigger = wstring_to_utf8str(raw_text); - std::string utf8_out_str(utf8_trigger); - - if (LLGestureMgr::instance().matchPrefix(utf8_trigger, &utf8_out_str)) - { - std::string rest_of_match = utf8_out_str.substr(utf8_trigger.size()); - if (!rest_of_match.empty()) - { - caller->setText(utf8_trigger + rest_of_match); // keep original capitalization for user-entered part - - // Select to end of line, starting from the character - // after the last one the user typed. - S32 outlength = caller->getLength(); // in characters - caller->setSelection(length, outlength); - caller->setCursor(outlength); - } - } - else if (matchChatTypeTrigger(utf8_trigger, &utf8_out_str)) - { - std::string rest_of_match = utf8_out_str.substr(utf8_trigger.size()); - caller->setText(utf8_trigger + rest_of_match + " "); // keep original capitalization for user-entered part - 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 && mask != MASK_CONTROL) - { - S32 cur_pos = caller->getCursor(); - if (cur_pos && (raw_text[cur_pos - 1] != ' ')) - { - // Get a list of avatars within range - uuid_vec_t avatar_ids; - LLWorld::getInstance()->getAvatars(&avatar_ids, NULL, gAgent.getPositionGlobal(), gSavedSettings.getF32("NearMeRange")); - - if (avatar_ids.empty()) return; // Nobody's in range! - - // Parse text for a pattern to search - std::string prefix = wstring_to_utf8str(raw_text.substr(0, cur_pos)); // Text before search string - std::string suffix = ""; - if (cur_pos <= raw_text.length()) // Is there anything after the cursor? - { - suffix = wstring_to_utf8str(raw_text.substr(cur_pos)); // Text after search string - } - size_t 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); - std::string match = pattern; - LLStringUtil::toLower(pattern); - - std::string name; - bool found = false; - bool full_name = false; - uuid_vec_t::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 (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 - prefix += match_pattern; - } - 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 (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 first_name, last_name; - gCacheName->getFirstLastName(*(iter - 1), first_name, last_name); - std::string rest_of_match; - std::string replaced_text; - if (gRlvHandler.hasBehaviour(RLV_BHVR_SHOWNAMES)) - { - replaced_text += RlvStrings::getAnonym(first_name + " " + last_name) + " "; - } - else - { - if (full_name) - { - rest_of_match = /*first_name + " " +*/ last_name.substr(pattern.size()); - } - else - { - rest_of_match = first_name.substr(pattern.size()); - } - replaced_text += match + rest_of_match + " "; - } - if (!rest_of_match.empty()) - { - caller->setText(prefix + replaced_text + suffix); - caller->setSelection(utf8str_to_wstring(prefix + replaced_text).length(), cur_pos); - } - } - } - } - // -} - -BOOL FSNearbyChatControl::matchChatTypeTrigger(const std::string& in_str, std::string* out_str) -{ - U32 in_len = in_str.length(); - S32 cnt = sizeof(sChatTypeTriggers) / sizeof(*sChatTypeTriggers); - - for (S32 n = 0; n < cnt; n++) - { - if (in_len > sChatTypeTriggers[n].name.length()) - continue; - - std::string trigger_trunc = sChatTypeTriggers[n].name; - LLStringUtil::truncate(trigger_trunc, in_len); - - if (!LLStringUtil::compareInsensitive(in_str, trigger_trunc)) - { - *out_str = sChatTypeTriggers[n].name; - return TRUE; - } - } - - return FALSE; + FSNearbyChat::handleChatBarKeystroke(caller); } // send our focus status to the LLNearbyChat hub void FSNearbyChatControl::onFocusReceived() { - FSNearbyChat::instance().setFocusedInputEditor(this,TRUE); + FSNearbyChat::instance().setFocusedInputEditor(this, TRUE); LLLineEditor::onFocusReceived(); } void FSNearbyChatControl::onFocusLost() { - FSNearbyChat::instance().setFocusedInputEditor(this,FALSE); + FSNearbyChat::instance().setFocusedInputEditor(this, FALSE); LLLineEditor::onFocusLost(); } void FSNearbyChatControl::setFocus(BOOL focus) { - FSNearbyChat::instance().setFocusedInputEditor(this,focus); + FSNearbyChat::instance().setFocusedInputEditor(this, focus); LLLineEditor::setFocus(focus); } void FSNearbyChatControl::autohide() { - if(getName()=="default_chat_bar") + if (getName() == "default_chat_bar") { - if(gSavedSettings.getBOOL("CloseChatOnReturn")) + if (gSavedSettings.getBOOL("CloseChatOnReturn")) { setFocus(FALSE); } - if(gAgentCamera.cameraMouselook() || gSavedSettings.getBOOL("AutohideChatBar")) + if (gAgentCamera.cameraMouselook() || gSavedSettings.getBOOL("AutohideChatBar")) { FSNearbyChat::instance().showDefaultChatBar(FALSE); } @@ -338,59 +108,58 @@ void FSNearbyChatControl::autohide() } // handle ESC key here -BOOL FSNearbyChatControl::handleKeyHere(KEY key, MASK mask ) +BOOL FSNearbyChatControl::handleKeyHere(KEY key, MASK mask) { - BOOL handled = FALSE; + bool handled = false; EChatType type = CHAT_TYPE_NORMAL; // autohide the chat bar if escape key was pressed and we're the default chat bar - if(key==KEY_ESCAPE && mask==MASK_NONE) + if (key == KEY_ESCAPE && mask == MASK_NONE) { - // we let ESC key go through to the rest of the UI code, so don't set handled=TRUE + // we let ESC key go through to the rest of the UI code, so don't set handled = true autohide(); gAgent.stopTyping(); } - else if( KEY_RETURN == key ) + else if (KEY_RETURN == key) { if (mask == MASK_CONTROL && gSavedSettings.getBOOL("FSUseCtrlShout")) { // shout type = CHAT_TYPE_SHOUT; - handled = TRUE; + handled = true; } else if (mask == MASK_SHIFT && gSavedSettings.getBOOL("FSUseShiftWhisper")) { // whisper type = CHAT_TYPE_WHISPER; - handled = TRUE; + handled = true; } else if (mask == MASK_ALT && gSavedSettings.getBOOL("FSUseAltOOC")) { // OOC type = CHAT_TYPE_OOC; - handled = TRUE; + handled = true; } else if (mask == (MASK_SHIFT | MASK_CONTROL)) { addChar(llwchar(182)); - return TRUE; } else { // say type = CHAT_TYPE_NORMAL; - handled = TRUE; + handled = true; } } - if (handled == TRUE) + if (handled) { // save current line in the history buffer LLLineEditor::onCommit(); // send chat to nearby chat hub - FSNearbyChat::instance().sendChat(getConvertedText(),type); + FSNearbyChat::instance().sendChat(getConvertedText(), type); setText(LLStringExplicit("")); autohide(); @@ -398,5 +167,5 @@ BOOL FSNearbyChatControl::handleKeyHere(KEY key, MASK mask ) } // let the line editor handle everything we don't handle - return LLLineEditor::handleKeyHere(key,mask); + return LLLineEditor::handleKeyHere(key, mask); } diff --git a/indra/newview/fsnearbychatcontrol.h b/indra/newview/fsnearbychatcontrol.h index f8af8bc156..43a0b22eba 100644 --- a/indra/newview/fsnearbychatcontrol.h +++ b/indra/newview/fsnearbychatcontrol.h @@ -43,13 +43,11 @@ public: virtual void onFocusLost(); virtual void setFocus(BOOL focus); - virtual BOOL handleKeyHere(KEY key,MASK mask); - - static BOOL matchChatTypeTrigger(const std::string& in_str, std::string* out_str); + virtual BOOL handleKeyHere(KEY key, MASK mask); private: // Typing in progress, expand gestures etc. - static void onKeystroke(LLLineEditor* caller,void* userdata); + static void onKeystroke(LLLineEditor* caller, void* userdata); // Unfocus and autohide chat bar accordingly if we are the default chat bar void autohide(); diff --git a/indra/newview/fsnearbychathub.cpp b/indra/newview/fsnearbychathub.cpp index 11e2b13cbb..890b464f7f 100644 --- a/indra/newview/fsnearbychathub.cpp +++ b/indra/newview/fsnearbychathub.cpp @@ -35,13 +35,19 @@ #include "fsnearbychatcontrol.h" #include "llagent.h" // gAgent #include "llanimationstates.h" // ANIM_AGENT_WHISPER, ANIM_AGENT_TALK, ANIM_AGENT_SHOUT +#include "llchatentry.h" #include "llcommandhandler.h" #include "llgesturemgr.h" +#include "lllineeditor.h" #include "llspinctrl.h" #include "llviewercontrol.h" +#include "llviewerkeyboard.h" #include "llviewerstats.h" +#include "llworld.h" #include "rlvhandler.h" +static const U32 NAME_PREDICTION_MINIMUM_LENGTH = 3; + // Fix for bad edge snapping // *HACK* chat bar cannot return its correct height for some reason static const S32 MAGIC_CHAT_BAR_PAD = 5; @@ -583,6 +589,275 @@ BOOL FSNearbyChat::defaultChatBarHasFocus() const return FALSE; } +bool matchChatTypeTrigger(const std::string& in_str, std::string* out_str) +{ + U32 in_len = in_str.length(); + S32 cnt = sizeof(sChatTypeTriggers) / sizeof(*sChatTypeTriggers); + + for (S32 n = 0; n < cnt; n++) + { + if (in_len > sChatTypeTriggers[n].name.length()) + continue; + + std::string trigger_trunc = sChatTypeTriggers[n].name; + LLStringUtil::truncate(trigger_trunc, in_len); + + if (!LLStringUtil::compareInsensitive(in_str, trigger_trunc)) + { + *out_str = sChatTypeTriggers[n].name; + return true; + } + } + + return false; +} + +//static +void FSNearbyChat::handleChatBarKeystroke(LLUICtrl* source) +{ + LLChatEntry* chat_entry = dynamic_cast(source); + LLLineEditor* line_editor = dynamic_cast(source); + + if (!chat_entry && !line_editor) + { + return; + } + + LLWString raw_text; + if (chat_entry) + { + raw_text = chat_entry->getWText(); + } + else + { + raw_text = line_editor->getWText(); + } + + // Can't trim the end, because that will cause autocompletion + // to eat trailing spaces that might be part of a gesture. + LLWStringUtil::trimHead(raw_text); + S32 length = raw_text.length(); + + S32 channel=0; + if (gSavedSettings.getBOOL("FSNearbyChatbar") && + gSavedSettings.getBOOL("FSShowChatChannel")) + { + // [FS communication UI] + //channel = (S32)(LLFloaterNearbyChat::getInstance()->getChild("ChatChannel")->get()); + channel = (S32)(FSFloaterNearbyChat::getInstance()->getChild("ChatChannel")->get()); + // [FS communication UI] + } + + // if( (length > 0) && (raw_text[0] != '/') ) // forward slash is used for escape (eg. emote) sequences + // [RLVa:KB] - Checked: 2010-03-26 (RLVa-1.2.0b) | Modified: RLVa-1.0.0d + if (length > 0 && + raw_text[0] != '/' && (raw_text[0] != ':' || !gSavedSettings.getBOOL("AllowMUpose")) && + !gRlvHandler.hasBehaviour(RLV_BHVR_REDIRCHAT)) + // [/RLVa:KB] + { + // only start typing animation if we are chatting without / on channel 0 -Zi + if(channel==0) + gAgent.startTyping(); + } + else + { + gAgent.stopTyping(); + } + + KEY key = gKeyboard->currentKey(); + MASK mask = gKeyboard->currentMask(FALSE); + + // Ignore "special" keys, like backspace, arrows, etc. + if (length > 1 + && raw_text[0] == '/' + && key < KEY_SPECIAL + && gSavedSettings.getBOOL("FSChatbarGestureAutoCompleteEnable")) + { + // we're starting a gesture, attempt to autocomplete + + std::string utf8_trigger = wstring_to_utf8str(raw_text); + std::string utf8_out_str(utf8_trigger); + + if (LLGestureMgr::instance().matchPrefix(utf8_trigger, &utf8_out_str)) + { + std::string rest_of_match = utf8_out_str.substr(utf8_trigger.size()); + if (!rest_of_match.empty()) + { + if (chat_entry) + { + chat_entry->setText(utf8_trigger + rest_of_match); // keep original capitalization for user-entered part + + // Select to end of line, starting from the character + // after the last one the user typed. + chat_entry->selectByCursorPosition(utf8_out_str.size()-rest_of_match.size(),utf8_out_str.size()); + } + else + { + line_editor->setText(utf8_trigger + rest_of_match); // keep original capitalization for user-entered part + + // Select to end of line, starting from the character + // after the last one the user typed. + S32 outlength = line_editor->getLength(); // in characters + line_editor->setSelection(length, outlength); + line_editor->setCursor(outlength); + } + } + } + else if (matchChatTypeTrigger(utf8_trigger, &utf8_out_str)) + { + std::string rest_of_match = utf8_out_str.substr(utf8_trigger.size()); + if (chat_entry) + { + chat_entry->setText(utf8_trigger + rest_of_match + " "); // keep original capitalization for user-entered part + chat_entry->endOfDoc(); + } + else + { + line_editor->setText(utf8_trigger + rest_of_match + " "); // keep original capitalization for user-entered part + line_editor->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 && mask != MASK_CONTROL) + { + S32 cur_pos; + if (chat_entry) + { + cur_pos = chat_entry->getCursorPos(); + } + else + { + cur_pos = line_editor->getCursor(); + } + if (cur_pos && (raw_text[cur_pos - 1] != ' ')) + { + // Get a list of avatars within range + uuid_vec_t avatar_ids; + LLWorld::getInstance()->getAvatars(&avatar_ids, NULL, gAgent.getPositionGlobal(), gSavedSettings.getF32("NearMeRange")); + + if (avatar_ids.empty()) return; // Nobody's in range! + + // Parse text for a pattern to search + std::string prefix = wstring_to_utf8str(raw_text.substr(0, cur_pos)); // Text before search string + std::string suffix = ""; + if (cur_pos <= raw_text.length()) // Is there anything after the cursor? + { + suffix = wstring_to_utf8str(raw_text.substr(cur_pos)); // Text after search string + } + size_t 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); + std::string match = pattern; + LLStringUtil::toLower(pattern); + + std::string name; + bool found = false; + bool full_name = false; + uuid_vec_t::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 (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 + prefix += match_pattern; + } + 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 (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 first_name, last_name; + gCacheName->getFirstLastName(*(iter - 1), first_name, last_name); + std::string rest_of_match; + std::string replaced_text; + if (gRlvHandler.hasBehaviour(RLV_BHVR_SHOWNAMES)) + { + replaced_text += RlvStrings::getAnonym(first_name + " " + last_name) + " "; + } + else + { + if (full_name) + { + rest_of_match = /*first_name + " " +*/ last_name.substr(pattern.size()); + } + else + { + rest_of_match = first_name.substr(pattern.size()); + } + replaced_text += match + rest_of_match + " "; + } + if (!rest_of_match.empty()) + { + if (chat_entry) + { + chat_entry->setText(prefix + replaced_text + suffix); + chat_entry->selectByCursorPosition(utf8string_to_wstring(prefix).size() + utf8string_to_wstring(match).size(), utf8string_to_wstring(prefix).size() + utf8string_to_wstring(replaced_text).size()); + } + else + { + line_editor->setText(prefix + replaced_text + suffix); + line_editor->setSelection(utf8str_to_wstring(prefix + replaced_text).length(), cur_pos); + } + } + } + } + } + // +} + class LLChatCommandHandler : public LLCommandHandler { diff --git a/indra/newview/fsnearbychathub.h b/indra/newview/fsnearbychathub.h index b205183f2e..9d89cbc9dd 100644 --- a/indra/newview/fsnearbychathub.h +++ b/indra/newview/fsnearbychathub.h @@ -32,6 +32,7 @@ #include "llviewerchat.h" class FSNearbyChatControl; +class LLUICtrl; class FSNearbyChat : public LLSingleton { @@ -52,19 +53,21 @@ public: void registerChatBar(FSNearbyChatControl* chatBar); // set the contents of the chat bar to "text" if it was empty, otherwise just show it - void showDefaultChatBar(BOOL visible,const char* text=0) const; + void showDefaultChatBar(BOOL visible, const char* text = NULL) const; - void sendChat(LLWString text,EChatType type); + void sendChat(LLWString text, EChatType type); static LLWString stripChannelNumber(const LLWString &mesg, S32* channel, S32* last_channel, bool* is_set); EChatType processChatTypeTriggers(EChatType type, std::string &str); void sendChatFromViewer(const std::string& utf8text, EChatType type, BOOL animate); void sendChatFromViewer(const LLWString& wtext, EChatType type, BOOL animate); - void setFocusedInputEditor(FSNearbyChatControl* inputEditor,BOOL focus); + void setFocusedInputEditor(FSNearbyChatControl* inputEditor, BOOL focus); BOOL defaultChatBarIsIdle() const; BOOL defaultChatBarHasFocus() const; + static void handleChatBarKeystroke(LLUICtrl* source); + FSNearbyChatControl* mFocusedInputEditor; };