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;
	}
}
예제 #2
0
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>
}