void LLNearbyChatBar::onChatBoxKeystroke(LLLineEditor* caller, void* userdata) { LLFirstUse::otherAvatarChatFirst(false); LLNearbyChatBar* self = (LLNearbyChatBar *)userdata; LLWString raw_text = self->mChatBox->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(); // 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] != '/') && (!gRlvHandler.hasBehaviour(RLV_BHVR_REDIRCHAT)) ) // [/RLVa:KB] { gAgent.startTyping(); } else { gAgent.stopTyping(); } /* Doesn't work -- can't tell the difference between a backspace that killed the selection vs. backspace at the end of line. if (length > 1 && text[0] == '/' && key == KEY_BACKSPACE) { // the selection will already be deleted, but we need to trim // off the character before std::string new_text = raw_text.substr(0, length-1); self->mInputEditor->setText( new_text ); self->mInputEditor->setCursorToEnd(); length = length - 1; } */ KEY key = gKeyboard->currentKey(); // Ignore "special" keys, like backspace, arrows, etc. if (length > 1 && raw_text[0] == '/' && key < KEY_SPECIAL) { // 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()); self->mChatBox->setText(utf8_trigger + rest_of_match); // keep original capitalization for user-entered part S32 outlength = self->mChatBox->getLength(); // in characters // Select to end of line, starting from the character // after the last one the user typed. self->mChatBox->setSelection(length, outlength); } else if (matchChatTypeTrigger(utf8_trigger, &utf8_out_str)) { std::string rest_of_match = utf8_out_str.substr(utf8_trigger.size()); self->mChatBox->setText(utf8_trigger + rest_of_match + " "); // keep original capitalization for user-entered part self->mChatBox->setCursorToEnd(); } //llinfos << "GESTUREDEBUG " << trigger // << " len " << length // << " outlen " << out_str.getLength() // << llendl; } }
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:Ansariel> [FS communication UI] //channel = (S32)(LLFloaterNearbyChat::getInstance()->getChild<LLSpinCtrl>("ChatChannel")->get()); channel = (S32)(FSFloaterNearbyChat::getInstance()->getChild<LLSpinCtrl>("ChatChannel")->get()); // </FS:Ansariel> [FS communication UI] } // -Zi // 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] != '/') && (!(gSavedSettings.getBOOL("AllowMUpose") && raw_text[0] == ':')) && (!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(); } } // <FS:CR> FIRE-3192 - Predictive name completion, based on code by Satomi Ahn static LLCachedControl<bool> 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 std::vector<LLUUID> avatar_ids; std::vector<LLVector3d> 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 = 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; std::vector<LLUUID>::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 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 ((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 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(prefix.size() + match.size(), prefix.size() + replaced_text.size()); } } } } // </FS:CR> }