Maintenance: Get rid of code duplication in FSFloaterNearbyChat::onChatBoxKeystroke() and FSNearbyChatControl::onKeystroke()
parent
ad3ae1f366
commit
4b02740f09
|
|
@ -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: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]
|
||||
}
|
||||
|
||||
// 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();
|
||||
}
|
||||
}
|
||||
|
||||
// <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
|
||||
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());
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
// </FS:CR>
|
||||
FSNearbyChat::handleChatBarKeystroke(mInputEditor);
|
||||
}
|
||||
|
||||
// static
|
||||
|
|
|
|||
|
|
@ -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();
|
||||
|
|
|
|||
|
|
@ -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" // <FS:CR> FIRE-3192 - Name Prediction
|
||||
#include "rlvhandler.h"
|
||||
|
||||
static const U32 NAME_PREDICTION_MINIMUM_LENGTH = 3;
|
||||
|
||||
static LLDefaultChildRegistry::Register<FSNearbyChatControl> 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)
|
||||
{
|
||||
//<FS:TS> 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: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]
|
||||
}
|
||||
|
||||
// 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();
|
||||
}
|
||||
}
|
||||
|
||||
// <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 = 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);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
// </FS:CR>
|
||||
}
|
||||
|
||||
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);
|
||||
}
|
||||
|
|
|
|||
|
|
@ -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();
|
||||
|
|
|
|||
|
|
@ -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;
|
||||
|
||||
// <FS:KC> 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<LLChatEntry*>(source);
|
||||
LLLineEditor* line_editor = dynamic_cast<LLLineEditor*>(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: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]
|
||||
}
|
||||
|
||||
// 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();
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// <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;
|
||||
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);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
// </FS:CR>
|
||||
}
|
||||
|
||||
|
||||
class LLChatCommandHandler : public LLCommandHandler
|
||||
{
|
||||
|
|
|
|||
|
|
@ -32,6 +32,7 @@
|
|||
#include "llviewerchat.h"
|
||||
|
||||
class FSNearbyChatControl;
|
||||
class LLUICtrl;
|
||||
|
||||
class FSNearbyChat : public LLSingleton<FSNearbyChat>
|
||||
{
|
||||
|
|
@ -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;
|
||||
};
|
||||
|
||||
|
|
|
|||
Loading…
Reference in New Issue