diff options
Diffstat (limited to 'indra/newview/llgesturemgr.cpp')
| -rw-r--r-- | indra/newview/llgesturemgr.cpp | 3095 | 
1 files changed, 1580 insertions, 1515 deletions
diff --git a/indra/newview/llgesturemgr.cpp b/indra/newview/llgesturemgr.cpp index 20b3c94232..fcafd949c0 100644 --- a/indra/newview/llgesturemgr.cpp +++ b/indra/newview/llgesturemgr.cpp @@ -1,1515 +1,1580 @@ -/**  - * @file llgesturemgr.cpp - * @brief Manager for playing gestures on the viewer - * - * $LicenseInfo:firstyear=2004&license=viewerlgpl$ - * Second Life Viewer Source Code - * Copyright (C) 2010, Linden Research, Inc. - *  - * This library is free software; you can redistribute it and/or - * modify it under the terms of the GNU Lesser General Public - * License as published by the Free Software Foundation; - * version 2.1 of the License only. - *  - * This library is distributed in the hope that it will be useful, - * but WITHOUT ANY WARRANTY; without even the implied warranty of - * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU - * Lesser General Public License for more details. - *  - * You should have received a copy of the GNU Lesser General Public - * License along with this library; if not, write to the Free Software - * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA  02110-1301  USA - *  - * Linden Research, Inc., 945 Battery Street, San Francisco, CA  94111  USA - * $/LicenseInfo$ - */ - -#include "llviewerprecompiledheaders.h" - -#include "llgesturemgr.h" - -// system -#include <functional> -#include <algorithm> - -// library -#include "llaudioengine.h" -#include "lldatapacker.h" -#include "llfloaterreg.h" -#include "llinventory.h" -#include "llkeyframemotion.h" -#include "llmultigesture.h" -#include "llnotificationsutil.h" -#include "llstl.h" -#include "llstring.h"	// todo: remove -#include "llfilesystem.h" -#include "message.h" - -// newview -#include "llagent.h" -#include "lldelayedgestureerror.h" -#include "llinventorymodel.h" -#include "llviewermessage.h" -#include "llvoavatarself.h" -#include "llviewerstats.h" -#include "llfloaterimnearbychat.h" -#include "llappearancemgr.h" -#include "llgesturelistener.h" - -// Longest time, in seconds, to wait for all animations to stop playing -const F32 MAX_WAIT_ANIM_SECS = 30.f; - -// Lightweight constructor. -// init() does the heavy lifting. -LLGestureMgr::LLGestureMgr() -:	mValid(false), -	mPlaying(), -	mActive(), -	mLoadingCount(0) -{ -	gInventory.addObserver(this); -	mListener.reset(new LLGestureListener()); -} - - -// We own the data for gestures, so clean them up. -LLGestureMgr::~LLGestureMgr() -{ -	item_map_t::iterator it; -	for (it = mActive.begin(); it != mActive.end(); ++it) -	{ -		LLMultiGesture* gesture = (*it).second; - -		delete gesture; -		gesture = NULL; -	} -	gInventory.removeObserver(this); -} - - -void LLGestureMgr::init() -{ -	// TODO -} - -void LLGestureMgr::changed(U32 mask)  -{  -	LLInventoryFetchItemsObserver::changed(mask); - -	if (mask & LLInventoryObserver::GESTURE) -	{ -		// If there was a gesture label changed, update all the names in the  -		// active gestures and then notify observers -		if (mask & LLInventoryObserver::LABEL) -		{ -			for(item_map_t::iterator it = mActive.begin(); it != mActive.end(); ++it) -			{ -				if(it->second) -				{ -					LLViewerInventoryItem* item = gInventory.getItem(it->first); -					if(item) -					{ -						it->second->mName = item->getName(); -					} -				} -			} -			notifyObservers(); -		} -		// If there was a gesture added or removed notify observers -		// STRUCTURE denotes that the inventory item has been moved -		// In the case of deleting gesture, it is moved to the trash -		else if(mask & LLInventoryObserver::ADD || -				mask & LLInventoryObserver::REMOVE || -				mask & LLInventoryObserver::STRUCTURE) -		{ -			notifyObservers(); -		} -	} -} - - -// Use this version when you have the item_id but not the asset_id, -// and you KNOW the inventory is loaded. -void LLGestureMgr::activateGesture(const LLUUID& item_id) -{ -	LLViewerInventoryItem* item = gInventory.getItem(item_id); -	if (!item) return; -	if (item->getType() != LLAssetType::AT_GESTURE) -		return; - -	LLUUID asset_id = item->getAssetUUID(); - -	mLoadingCount = 1; -	mDeactivateSimilarNames.clear(); - -	const bool inform_server = true; -	const bool deactivate_similar = false;  -	activateGestureWithAsset(item_id, asset_id, inform_server, deactivate_similar); -} - - -void LLGestureMgr::activateGestures(LLViewerInventoryItem::item_array_t& items) -{ -	// Load up the assets -	S32 count = 0; -	LLViewerInventoryItem::item_array_t::const_iterator it; -	for (it = items.begin(); it != items.end(); ++it) -	{ -		LLViewerInventoryItem* item = *it; - -		if (isGestureActive(item->getUUID())) -		{ -			continue; -		} -		else  -		{ // Make gesture active and persistent through login sessions.  -Aura 07-12-06 -			activateGesture(item->getUUID()); -		} - -		count++; -	} - -	mLoadingCount = count; -	mDeactivateSimilarNames.clear(); - -	for (it = items.begin(); it != items.end(); ++it) -	{ -		LLViewerInventoryItem* item = *it; - -		if (isGestureActive(item->getUUID())) -		{ -			continue; -		} - -		// Don't inform server, we'll do that in bulk -		const bool no_inform_server = false; -		const bool deactivate_similar = true; -		activateGestureWithAsset(item->getUUID(), item->getAssetUUID(), -								 no_inform_server, -								 deactivate_similar); -	} - -	// Inform the database of this change -	LLMessageSystem* msg = gMessageSystem; - -	bool start_message = true; - -	for (it = items.begin(); it != items.end(); ++it) -	{ -		LLViewerInventoryItem* item = *it; - -		if (isGestureActive(item->getUUID())) -		{ -			continue; -		} - -		if (start_message) -		{ -			msg->newMessage("ActivateGestures"); -			msg->nextBlock("AgentData"); -			msg->addUUID("AgentID", gAgent.getID()); -			msg->addUUID("SessionID", gAgent.getSessionID()); -			msg->addU32("Flags", 0x0); -			start_message = false; -		} -		 -		msg->nextBlock("Data"); -		msg->addUUID("ItemID", item->getUUID()); -		msg->addUUID("AssetID", item->getAssetUUID()); -		msg->addU32("GestureFlags", 0x0); - -		if (msg->getCurrentSendTotal() > MTUBYTES) -		{ -			gAgent.sendReliableMessage(); -			start_message = true; -		} -	} - -	if (!start_message) -	{ -		gAgent.sendReliableMessage(); -	} -} - - -struct LLLoadInfo -{ -	LLUUID mItemID; -	bool mInformServer; -	bool mDeactivateSimilar; -}; - -// If inform_server is true, will send a message upstream to update -// the user_gesture_active table. -/** - * It will load a gesture from remote storage - */ -void LLGestureMgr::activateGestureWithAsset(const LLUUID& item_id, -												const LLUUID& asset_id, -												bool inform_server, -												bool deactivate_similar) -{ -	const LLUUID& base_item_id = gInventory.getLinkedItemID(item_id); - -	if( !gAssetStorage ) -	{ -		LL_WARNS() << "LLGestureMgr::activateGestureWithAsset without valid gAssetStorage" << LL_ENDL; -		return; -	} -	// If gesture is already active, nothing to do. -	if (isGestureActive(item_id)) -	{ -		LL_WARNS() << "Tried to loadGesture twice " << item_id << LL_ENDL; -		return; -	} - -//	if (asset_id.isNull()) -//	{ -//		LL_WARNS() << "loadGesture() - gesture has no asset" << LL_ENDL; -//		return; -//	} - -	// For now, put NULL into the item map.  We'll build a gesture -	// class object when the asset data arrives. -	mActive[base_item_id] = NULL; - -	// Copy the UUID -	if (asset_id.notNull()) -	{ -		LLLoadInfo* info = new LLLoadInfo; -		info->mItemID = base_item_id; -		info->mInformServer = inform_server; -		info->mDeactivateSimilar = deactivate_similar; - -		const bool high_priority = true; -		gAssetStorage->getAssetData(asset_id, -									LLAssetType::AT_GESTURE, -									onLoadComplete, -									(void*)info, -									high_priority); -	} -	else -	{ -		notifyObservers(); -	} -} - - -void notify_update_label(const LLUUID& base_item_id) -{ -	gInventory.addChangedMask(LLInventoryObserver::LABEL, base_item_id); -	LLGestureMgr::instance().notifyObservers(); -} - -void LLGestureMgr::deactivateGesture(const LLUUID& item_id) -{ -	const LLUUID& base_item_id = gInventory.getLinkedItemID(item_id); -	item_map_t::iterator it = mActive.find(base_item_id); -	if (it == mActive.end()) -	{ -		LL_WARNS() << "deactivateGesture for inactive gesture " << item_id << LL_ENDL; -		return; -	} - -	// mActive owns this gesture pointer, so clean up memory. -	LLMultiGesture* gesture = (*it).second; - -	// Can be NULL gestures in the map -	if (gesture) -	{ -		stopGesture(gesture); - -		delete gesture; -		gesture = NULL; -	} - -	mActive.erase(it); - -	// Inform the database of this change -	LLMessageSystem* msg = gMessageSystem; -	msg->newMessage("DeactivateGestures"); -	msg->nextBlock("AgentData"); -	msg->addUUID("AgentID", gAgent.getID()); -	msg->addUUID("SessionID", gAgent.getSessionID()); -	msg->addU32("Flags", 0x0); -	 -	msg->nextBlock("Data"); -	msg->addUUID("ItemID", item_id); -	msg->addU32("GestureFlags", 0x0); - -	gAgent.sendReliableMessage(); - -	LLPointer<LLInventoryCallback> cb = -		new LLBoostFuncInventoryCallback(no_op_inventory_func, -										 boost::bind(notify_update_label,base_item_id)); - -	LLAppearanceMgr::instance().removeCOFItemLinks(base_item_id, cb); -} - - -void LLGestureMgr::deactivateSimilarGestures(LLMultiGesture* in, const LLUUID& in_item_id) -{ -	const LLUUID& base_in_item_id = gInventory.getLinkedItemID(in_item_id); -	uuid_vec_t gest_item_ids; - -	// Deactivate all gestures that match -	item_map_t::iterator it; -	for (it = mActive.begin(); it != mActive.end(); ) -	{ -		const LLUUID& item_id = (*it).first; -		LLMultiGesture* gest = (*it).second; - -		// Don't deactivate the gesture we are looking for duplicates of -		// (for replaceGesture) -		if (!gest || item_id == base_in_item_id)  -		{ -			// legal, can have null pointers in list -			++it; -		} -		else if ((!gest->mTrigger.empty() && gest->mTrigger == in->mTrigger) -				 || (gest->mKey != KEY_NONE && gest->mKey == in->mKey && gest->mMask == in->mMask)) -		{ -			gest_item_ids.push_back(item_id); - -			stopGesture(gest); - -			delete gest; -			gest = NULL; - -			mActive.erase(it++); -			gInventory.addChangedMask(LLInventoryObserver::LABEL, item_id); - -		} -		else -		{ -			++it; -		} -	} - -	// Inform database of the change -	LLMessageSystem* msg = gMessageSystem; -	bool start_message = true; -	uuid_vec_t::const_iterator vit = gest_item_ids.begin(); -	while (vit != gest_item_ids.end()) -	{ -		if (start_message) -		{ -			msg->newMessage("DeactivateGestures"); -			msg->nextBlock("AgentData"); -			msg->addUUID("AgentID", gAgent.getID()); -			msg->addUUID("SessionID", gAgent.getSessionID()); -			msg->addU32("Flags", 0x0); -			start_message = false; -		} -	 -		msg->nextBlock("Data"); -		msg->addUUID("ItemID", *vit); -		msg->addU32("GestureFlags", 0x0); - -		if (msg->getCurrentSendTotal() > MTUBYTES) -		{ -			gAgent.sendReliableMessage(); -			start_message = true; -		} - -		++vit; -	} - -	if (!start_message) -	{ -		gAgent.sendReliableMessage(); -	} - -	// Add to the list of names for the user. -	for (vit = gest_item_ids.begin(); vit != gest_item_ids.end(); ++vit) -	{ -		LLViewerInventoryItem* item = gInventory.getItem(*vit); -		if (!item) continue; - -		mDeactivateSimilarNames.append(item->getName()); -		mDeactivateSimilarNames.append("\n"); -	} - -	notifyObservers(); -} - - -bool LLGestureMgr::isGestureActive(const LLUUID& item_id) -{ -	const LLUUID& base_item_id = gInventory.getLinkedItemID(item_id); -	item_map_t::iterator it = mActive.find(base_item_id); -	return (it != mActive.end()); -} - - -bool LLGestureMgr::isGesturePlaying(const LLUUID& item_id) -{ -	const LLUUID& base_item_id = gInventory.getLinkedItemID(item_id); - -	item_map_t::iterator it = mActive.find(base_item_id); -	if (it == mActive.end()) return false; - -	LLMultiGesture* gesture = (*it).second; -	if (!gesture) return false; - -	return gesture->mPlaying; -} - -bool LLGestureMgr::isGesturePlaying(LLMultiGesture* gesture) -{ -	if(!gesture) -	{ -		return false; -	} - -	return gesture->mPlaying; -} - -void LLGestureMgr::replaceGesture(const LLUUID& item_id, LLMultiGesture* new_gesture, const LLUUID& asset_id) -{ -	const LLUUID& base_item_id = gInventory.getLinkedItemID(item_id); - -	item_map_t::iterator it = mActive.find(base_item_id); -	if (it == mActive.end()) -	{ -		LL_WARNS() << "replaceGesture for inactive gesture " << base_item_id << LL_ENDL; -		return; -	} - -	LLMultiGesture* old_gesture = (*it).second; -	stopGesture(old_gesture); - -	mActive.erase(base_item_id); - -	mActive[base_item_id] = new_gesture; - -    // replaceGesture(const LLUUID& item_id, const LLUUID& new_asset_id) -    // replaces ids without repalcing gesture -    if (old_gesture != new_gesture) -    { -        delete old_gesture; -        old_gesture = NULL; -    } - -	if (asset_id.notNull()) -	{ -		mLoadingCount = 1; -		mDeactivateSimilarNames.clear(); - -		LLLoadInfo* info = new LLLoadInfo; -		info->mItemID = base_item_id; -		info->mInformServer = true; -		info->mDeactivateSimilar = false; - -		const bool high_priority = true; -		gAssetStorage->getAssetData(asset_id, -									LLAssetType::AT_GESTURE, -									onLoadComplete, -									(void*)info, -									high_priority); -	} - -	notifyObservers(); -} - -void LLGestureMgr::replaceGesture(const LLUUID& item_id, const LLUUID& new_asset_id) -{ -	const LLUUID& base_item_id = gInventory.getLinkedItemID(item_id); - -	item_map_t::iterator it = LLGestureMgr::instance().mActive.find(base_item_id); -	if (it == mActive.end()) -	{ -		LL_WARNS() << "replaceGesture for inactive gesture " << base_item_id << LL_ENDL; -		return; -	} - -	// mActive owns this gesture pointer, so clean up memory. -	LLMultiGesture* gesture = (*it).second; -	LLGestureMgr::instance().replaceGesture(base_item_id, gesture, new_asset_id); -} - -void LLGestureMgr::playGesture(LLMultiGesture* gesture) -{ -	if (!gesture) return; - -	// Reset gesture to first step -	gesture->mCurrentStep = 0; - -	// Add to list of playing -	gesture->mPlaying = true; -	mPlaying.push_back(gesture); - -	// Load all needed assets to minimize the delays -	// when gesture is playing. -	for (std::vector<LLGestureStep*>::iterator steps_it = gesture->mSteps.begin(); -		 steps_it != gesture->mSteps.end(); -		 ++steps_it) -	{ -		LLGestureStep* step = *steps_it; -		switch(step->getType()) -		{ -		case STEP_ANIMATION: -			{ -				LLGestureStepAnimation* anim_step = (LLGestureStepAnimation*)step; -				const LLUUID& anim_id = anim_step->mAnimAssetID; - -				// Don't request the animation if this step stops it or if it is already in the cache -				if (!(anim_id.isNull() -					  || anim_step->mFlags & ANIM_FLAG_STOP -					  || gAssetStorage->hasLocalAsset(anim_id, LLAssetType::AT_ANIMATION))) -				{ -					mLoadingAssets.insert(anim_id); - -					LLUUID* id = new LLUUID(gAgentID); -					gAssetStorage->getAssetData(anim_id, -									LLAssetType::AT_ANIMATION, -									onAssetLoadComplete, -									(void *)id, -									true); -				} -				break; -			} -		case STEP_SOUND: -			{ -				LLGestureStepSound* sound_step = (LLGestureStepSound*)step; -				const LLUUID& sound_id = sound_step->mSoundAssetID; -				if (!(sound_id.isNull() -					  || gAssetStorage->hasLocalAsset(sound_id, LLAssetType::AT_SOUND))) -				{ -					mLoadingAssets.insert(sound_id); - -					gAssetStorage->getAssetData(sound_id, -									LLAssetType::AT_SOUND, -									onAssetLoadComplete, -									NULL, -									true); -				} -				break; -			} -		case STEP_CHAT: -		case STEP_WAIT: -		case STEP_EOF: -			{ -				break; -			} -		default: -			{ -				LL_WARNS() << "Unknown gesture step type: " << step->getType() << LL_ENDL; -			} -		} -	} - -	// And get it going -	stepGesture(gesture); - -	notifyObservers(); -} - - -// Convenience function that looks up the item_id for you. -void LLGestureMgr::playGesture(const LLUUID& item_id) -{ -	const LLUUID& base_item_id = gInventory.getLinkedItemID(item_id); - -	item_map_t::iterator it = mActive.find(base_item_id); -	if (it == mActive.end()) return; - -	LLMultiGesture* gesture = (*it).second; -	if (!gesture) return; - -	playGesture(gesture); -} - - -// Iterates through space delimited tokens in string, triggering any gestures found. -// Generates a revised string that has the found tokens replaced by their replacement strings -// and (as a minor side effect) has multiple spaces in a row replaced by single spaces. -bool LLGestureMgr::triggerAndReviseString(const std::string &utf8str, std::string* revised_string) -{ -	std::string tokenized = utf8str; - -	bool found_gestures = false; -	bool first_token = true; - -	typedef boost::tokenizer<boost::char_separator<char> > tokenizer; -	boost::char_separator<char> sep(" "); -	tokenizer tokens(tokenized, sep); -	tokenizer::iterator token_iter; - -	for( token_iter = tokens.begin(); token_iter != tokens.end(); ++token_iter) -	{ -		const char* cur_token = token_iter->c_str(); -		LLMultiGesture* gesture = NULL; - -		// Only pay attention to the first gesture in the string. -		if( !found_gestures ) -		{ -			// collect gestures that match -			std::vector <LLMultiGesture *> matching; -			item_map_t::iterator it; -			for (it = mActive.begin(); it != mActive.end(); ++it) -			{ -				gesture = (*it).second; - -				// Gesture asset data might not have arrived yet -				if (!gesture) continue; -				 -				if (LLStringUtil::compareInsensitive(gesture->mTrigger, cur_token) == 0) -				{ -					matching.push_back(gesture); -				} -				 -				gesture = NULL; -			} - -			 -			if (matching.size() > 0) -			{ -				// choose one at random -				{ -					S32 random = ll_rand(matching.size()); - -					gesture = matching[random]; -					 -					playGesture(gesture); - -					if (!gesture->mReplaceText.empty()) -					{ -						if( !first_token ) -						{ -							if (revised_string) -								revised_string->append( " " ); -						} - -						// Don't muck with the user's capitalization if we don't have to. -						if( LLStringUtil::compareInsensitive(cur_token, gesture->mReplaceText) == 0) -						{ -							if (revised_string) -								revised_string->append( cur_token ); -						} -						else -						{ -							if (revised_string) -								revised_string->append( gesture->mReplaceText ); -						} -					} -					found_gestures = true; -				} -			} -		} -		 -		if(!gesture) -		{ -			// This token doesn't match a gesture.  Pass it through to the output. -			if( !first_token ) -			{ -				if (revised_string) -					revised_string->append( " " ); -			} -			if (revised_string) -				revised_string->append( cur_token ); -		} - -		first_token = false; -		gesture = NULL; -	} -	return found_gestures; -} - - -bool LLGestureMgr::triggerGesture(KEY key, MASK mask) -{ -	std::vector <LLMultiGesture *> matching; -	item_map_t::iterator it; - -	// collect matching gestures -	for (it = mActive.begin(); it != mActive.end(); ++it) -	{ -		LLMultiGesture* gesture = (*it).second; - -		// asset data might not have arrived yet -		if (!gesture) continue; - -		if (gesture->mKey == key -			&& gesture->mMask == mask) -		{ -			matching.push_back(gesture); -		} -	} - -	// choose one and play it -	if (matching.size() > 0) -	{ -		U32 random = ll_rand(matching.size()); -		 -		LLMultiGesture* gesture = matching[random]; -			 -		playGesture(gesture); -		return true; -	} -	return false; -} - - -S32 LLGestureMgr::getPlayingCount() const -{ -	return mPlaying.size(); -} - - -struct IsGesturePlaying -{ -	bool operator()(const LLMultiGesture* gesture) const -	{ -		return bool(gesture->mPlaying); -	} -}; - -void LLGestureMgr::update() -{ -	S32 i; -	for (i = 0; i < (S32)mPlaying.size(); ++i) -	{ -		stepGesture(mPlaying[i]); -	} - -	// Clear out gestures that are done, by moving all the -	// ones that are still playing to the front. -	std::vector<LLMultiGesture*>::iterator new_end; -	new_end = std::partition(mPlaying.begin(), -							 mPlaying.end(), -							 IsGesturePlaying()); - -	// Something finished playing -	if (new_end != mPlaying.end()) -	{ -		// Delete the completed gestures that want deletion -		std::vector<LLMultiGesture*>::iterator it; -		for (it = new_end; it != mPlaying.end(); ++it) -		{ -			LLMultiGesture* gesture = *it; - -			if (gesture->mDoneCallback) -			{ -				gesture->mDoneCallback(gesture, gesture->mCallbackData); - -				// callback might have deleted gesture, can't -				// rely on this pointer any more -				gesture = NULL; -			} -		} - -		// And take done gestures out of the playing list -		mPlaying.erase(new_end, mPlaying.end()); - -		notifyObservers(); -	} -} - - -// Run all steps until you're either done or hit a wait. -void LLGestureMgr::stepGesture(LLMultiGesture* gesture) -{ -	if (!gesture) -	{ -		return; -	} -	if (!isAgentAvatarValid() || hasLoadingAssets(gesture)) return; - -	// Of the ones that started playing, have any stopped? - -	std::set<LLUUID>::iterator gest_it; -	for (gest_it = gesture->mPlayingAnimIDs.begin();  -		 gest_it != gesture->mPlayingAnimIDs.end();  -		 ) -	{ -		// look in signaled animations (simulator's view of what is -		// currently playing. -		LLVOAvatar::AnimIterator play_it = gAgentAvatarp->mSignaledAnimations.find(*gest_it); -		if (play_it != gAgentAvatarp->mSignaledAnimations.end()) -		{ -			++gest_it; -		} -		else -		{ -			// not found, so not currently playing or scheduled to play -			// delete from the triggered set -			gesture->mPlayingAnimIDs.erase(gest_it++); -		} -	} - -	// Of all the animations that we asked the sim to start for us, -	// pick up the ones that have actually started. -	for (gest_it = gesture->mRequestedAnimIDs.begin(); -		 gest_it != gesture->mRequestedAnimIDs.end(); -		 ) -	{ -	 LLVOAvatar::AnimIterator play_it = gAgentAvatarp->mSignaledAnimations.find(*gest_it); -		if (play_it != gAgentAvatarp->mSignaledAnimations.end()) -		{ -			// Hooray, this animation has started playing! -			// Copy into playing. -			gesture->mPlayingAnimIDs.insert(*gest_it); -			gesture->mRequestedAnimIDs.erase(gest_it++); -		} -		else -		{ -			// nope, not playing yet -			++gest_it; -		} -	} - -	// Run the current steps -	bool waiting = false; -	while (!waiting && gesture->mPlaying) -	{ -		// Get the current step, if there is one. -		// Otherwise enter the waiting at end state. -		LLGestureStep* step = NULL; -		if (gesture->mCurrentStep < (S32)gesture->mSteps.size()) -		{ -			step = gesture->mSteps[gesture->mCurrentStep]; -			llassert(step != NULL); -		} -		else -		{ -			// step stays null, we're off the end -			gesture->mWaitingAtEnd = true; -		} - - -		// If we're waiting at the end, wait for all gestures to stop -		// playing. -		// TODO: Wait for all sounds to complete as well. -		if (gesture->mWaitingAtEnd) -		{ -			// Neither do we have any pending requests, nor are they -			// still playing. -			if ((gesture->mRequestedAnimIDs.empty() -				&& gesture->mPlayingAnimIDs.empty())) -			{ -				// all animations are done playing -				gesture->mWaitingAtEnd = false; -				gesture->mPlaying = false; -			} -			else -			{ -				waiting = true; -			} -			continue; -		} - -		// If we're waiting on our animations to stop, poll for -		// completion. -		if (gesture->mWaitingAnimations) -		{ -			// Neither do we have any pending requests, nor are they -			// still playing. -			if ((gesture->mRequestedAnimIDs.empty() -				&& gesture->mPlayingAnimIDs.empty())) -			{ -				// all animations are done playing -				gesture->mWaitingAnimations = false; -				gesture->mCurrentStep++; -			} -			else if (gesture->mWaitTimer.getElapsedTimeF32() > MAX_WAIT_ANIM_SECS) -			{ -				// we've waited too long for an animation -				LL_INFOS("GestureMgr") << "Waited too long for animations to stop, continuing gesture." -					<< LL_ENDL; -				gesture->mWaitingAnimations = false; -				gesture->mCurrentStep++; -			} -			else -			{ -				waiting = true; -			} -			continue; -		} - -		// If we're waiting a fixed amount of time, check for timer -		// expiration. -		if (gesture->mWaitingTimer) -		{ -			// We're waiting for a certain amount of time to pass -			LLGestureStepWait* wait_step = (LLGestureStepWait*)step; - -			F32 elapsed = gesture->mWaitTimer.getElapsedTimeF32(); -			if (elapsed > wait_step->mWaitSeconds) -			{ -				// wait is done, continue execution -				gesture->mWaitingTimer = false; -				gesture->mCurrentStep++; -			} -			else -			{ -				// we're waiting, so execution is done for now -				waiting = true; -			} -			continue; -		} - -		// Not waiting, do normal execution -		runStep(gesture, step); -	} -} - - -void LLGestureMgr::runStep(LLMultiGesture* gesture, LLGestureStep* step) -{ -	switch(step->getType()) -	{ -	case STEP_ANIMATION: -		{ -			LLGestureStepAnimation* anim_step = (LLGestureStepAnimation*)step; -			if (anim_step->mAnimAssetID.isNull()) -			{ -				gesture->mCurrentStep++; -			} - -			if (anim_step->mFlags & ANIM_FLAG_STOP) -			{ -				gAgent.sendAnimationRequest(anim_step->mAnimAssetID, ANIM_REQUEST_STOP); -				// remove it from our request set in case we just requested it -				std::set<LLUUID>::iterator set_it = gesture->mRequestedAnimIDs.find(anim_step->mAnimAssetID); -				if (set_it != gesture->mRequestedAnimIDs.end()) -				{ -					gesture->mRequestedAnimIDs.erase(set_it); -				} -			} -			else -			{ -				gAgent.sendAnimationRequest(anim_step->mAnimAssetID, ANIM_REQUEST_START); -				// Indicate that we've requested this animation to play as -				// part of this gesture (but it won't start playing for at -				// least one round-trip to simulator). -				gesture->mRequestedAnimIDs.insert(anim_step->mAnimAssetID); -			} -			gesture->mCurrentStep++; -			break; -		} -	case STEP_SOUND: -		{ -			LLGestureStepSound* sound_step = (LLGestureStepSound*)step; -			const LLUUID& sound_id = sound_step->mSoundAssetID; -			const F32 volume = 1.f; -			send_sound_trigger(sound_id, volume); -			gesture->mCurrentStep++; -			break; -		} -	case STEP_CHAT: -		{ -			LLGestureStepChat* chat_step = (LLGestureStepChat*)step; -			std::string chat_text = chat_step->mChatText; -			// Don't animate the nodding, as this might not blend with -			// other playing animations. - -			const bool animate = false; - -			(LLFloaterReg::getTypedInstance<LLFloaterIMNearbyChat>("nearby_chat"))-> -					sendChatFromViewer(chat_text, CHAT_TYPE_NORMAL, animate); - -			gesture->mCurrentStep++; -			break; -		} -	case STEP_WAIT: -		{ -			LLGestureStepWait* wait_step = (LLGestureStepWait*)step; -			if (wait_step->mFlags & WAIT_FLAG_TIME) -			{ -				gesture->mWaitingTimer = true; -				gesture->mWaitTimer.reset(); -			} -			else if (wait_step->mFlags & WAIT_FLAG_ALL_ANIM) -			{ -				gesture->mWaitingAnimations = true; -				// Use the wait timer as a deadlock breaker for animation -				// waits. -				gesture->mWaitTimer.reset(); -			} -			else -			{ -				gesture->mCurrentStep++; -			} -			// Don't increment instruction pointer until wait is complete. -			break; -		} -	default: -		{ -			break; -		} -	} -} - - -// static -void LLGestureMgr::onLoadComplete(const LLUUID& asset_uuid, -								  LLAssetType::EType type, -								  void* user_data, S32 status, LLExtStat ext_status) -{ -	LLLoadInfo* info = (LLLoadInfo*)user_data; - -	LLUUID item_id = info->mItemID; -	bool inform_server = info->mInformServer; -	bool deactivate_similar = info->mDeactivateSimilar; - -	delete info; -	info = NULL; -	LLGestureMgr& self = LLGestureMgr::instance(); -	self.mLoadingCount--; - -	if (0 == status) -	{ -		LLFileSystem file(asset_uuid, type, LLFileSystem::READ); -		S32 size = file.getSize(); - -		std::vector<char> buffer(size+1); - -		file.read((U8*)&buffer[0], size); -		// ensure there's a trailing NULL so strlen will work. -		buffer[size] = '\0'; - -		LLMultiGesture* gesture = new LLMultiGesture(); - -		LLDataPackerAsciiBuffer dp(&buffer[0], size+1); -		bool ok = gesture->deserialize(dp); - -		if (ok) -		{ -			if (deactivate_similar) -			{ -				self.deactivateSimilarGestures(gesture, item_id); - -				// Display deactivation message if this was the last of the bunch. -				if (self.mLoadingCount == 0 -					&& self.mDeactivateSimilarNames.length() > 0) -				{ -					// we're done with this set of deactivations -					LLSD args; -					args["NAMES"] = self.mDeactivateSimilarNames; -					LLNotificationsUtil::add("DeactivatedGesturesTrigger", args); -				} -			} - -			LLViewerInventoryItem* item = gInventory.getItem(item_id); -			if(item) -			{ -				gesture->mName = item->getName(); -			} -			else -			{ -				// Watch this item and set gesture name when item exists in inventory -				self.setFetchID(item_id); -				self.startFetch(); -			} - -            item_map_t::iterator it = self.mActive.find(item_id); -            if (it == self.mActive.end()) -            { -                // Gesture is supposed to be present, active, but NULL -                LL_DEBUGS("GestureMgr") << "Gesture " << item_id << " not found in active list" << LL_ENDL; -            } -            else -            { -                LLMultiGesture* old_gesture = (*it).second; -                if (old_gesture && old_gesture != gesture) -                { -                    LL_DEBUGS("GestureMgr") << "Received dupplicate " << item_id << " callback" << LL_ENDL; -                    // In case somebody managest to activate, deactivate and -                    // then activate gesture again, before asset finishes loading. -                    // LLLoadInfo will have a different pointer, asset storage will -                    // see it as a different request, resulting in two callbacks. - -                    // deactivateSimilarGestures() did not turn this one off -                    // because of matching item_id -                    self.stopGesture(old_gesture); - -                    self.mActive.erase(item_id); -                    delete old_gesture; -                    old_gesture = NULL; -                } -            } - -			self.mActive[item_id] = gesture; - -			// Everything has been successful.  Add to the active list. -			gInventory.addChangedMask(LLInventoryObserver::LABEL, item_id); - -			if (inform_server) -			{ -				// Inform the database of this change -				LLMessageSystem* msg = gMessageSystem; -				msg->newMessage("ActivateGestures"); -				msg->nextBlock("AgentData"); -				msg->addUUID("AgentID", gAgent.getID()); -				msg->addUUID("SessionID", gAgent.getSessionID()); -				msg->addU32("Flags", 0x0); -				 -				msg->nextBlock("Data"); -				msg->addUUID("ItemID", item_id); -				msg->addUUID("AssetID", asset_uuid); -				msg->addU32("GestureFlags", 0x0); - -				gAgent.sendReliableMessage(); -			} -			callback_map_t::iterator i_cb = self.mCallbackMap.find(item_id); -			 -			if(i_cb != self.mCallbackMap.end()) -			{ -				i_cb->second(gesture); -				self.mCallbackMap.erase(i_cb); -			} - -			self.notifyObservers(); -		} -		else -		{ -			LL_WARNS("GestureMgr") << "Unable to load gesture" << LL_ENDL; - -            item_map_t::iterator it = self.mActive.find(item_id); -            if (it != self.mActive.end()) -            { -                LLMultiGesture* old_gesture = (*it).second; -                if (old_gesture) -                { -                    // Shouldn't happen, just in case -                    LL_WARNS("GestureMgr") << "Gesture " << item_id << " existed when it shouldn't" << LL_ENDL; - -                    self.stopGesture(old_gesture); -                    delete old_gesture; -                    old_gesture = NULL; -                } -                self.mActive.erase(item_id); -            } -			 -			delete gesture; -			gesture = NULL; -		} -	} -	else -	{ -		if( LL_ERR_ASSET_REQUEST_NOT_IN_DATABASE == status || -			LL_ERR_FILE_EMPTY == status) -		{ -			LLDelayedGestureError::gestureMissing( item_id ); -		} -		else -		{ -			LLDelayedGestureError::gestureFailedToLoad( item_id ); -		} - -		LL_WARNS("GestureMgr") << "Problem loading gesture: " << status << LL_ENDL; -         -        item_map_t::iterator it = self.mActive.find(item_id); -        if (it != self.mActive.end()) -        { -            LLMultiGesture* old_gesture = (*it).second; -            if (old_gesture) -            { -                // Shouldn't happen, just in case -                LL_WARNS("GestureMgr") << "Gesture " << item_id << " existed when it shouldn't" << LL_ENDL; - -                self.stopGesture(old_gesture); -                delete old_gesture; -                old_gesture = NULL; -            } -            self.mActive.erase(item_id); -        } -	} -} - -// static -void LLGestureMgr::onAssetLoadComplete(const LLUUID& asset_uuid, -									   LLAssetType::EType type, -									   void* user_data, S32 status, LLExtStat ext_status) -{ -	LLGestureMgr& self = LLGestureMgr::instance(); - -	// Complete the asset loading process depending on the type and -	// remove the asset id from pending downloads list. -	switch(type) -	{ -	case LLAssetType::AT_ANIMATION: -		{ -			LLKeyframeMotion::onLoadComplete(asset_uuid, type, user_data, status, ext_status); - -			self.mLoadingAssets.erase(asset_uuid); - -			break; -		} -	case LLAssetType::AT_SOUND: -		{ -			LLAudioEngine::assetCallback(asset_uuid, type, user_data, status, ext_status); - -			self.mLoadingAssets.erase(asset_uuid); - -			break; -		} -	default: -		{ -			LL_WARNS() << "Unexpected asset type: " << type << LL_ENDL; - -			// We don't want to return from this callback without -			// an animation or sound callback being fired -			// and *user_data handled to avoid memory leaks. -			llassert(type == LLAssetType::AT_ANIMATION || type == LLAssetType::AT_SOUND); -		} -	} -} - -// static -bool LLGestureMgr::hasLoadingAssets(LLMultiGesture* gesture) -{ -	LLGestureMgr& self = LLGestureMgr::instance(); - -	for (std::vector<LLGestureStep*>::iterator steps_it = gesture->mSteps.begin(); -		 steps_it != gesture->mSteps.end(); -		 ++steps_it) -	{ -		LLGestureStep* step = *steps_it; -		switch(step->getType()) -		{ -		case STEP_ANIMATION: -			{ -				LLGestureStepAnimation* anim_step = (LLGestureStepAnimation*)step; -				const LLUUID& anim_id = anim_step->mAnimAssetID; - -				if (!(anim_id.isNull() -					  || anim_step->mFlags & ANIM_FLAG_STOP -					  || self.mLoadingAssets.find(anim_id) == self.mLoadingAssets.end())) -				{ -					return true; -				} -				break; -			} -		case STEP_SOUND: -			{ -				LLGestureStepSound* sound_step = (LLGestureStepSound*)step; -				const LLUUID& sound_id = sound_step->mSoundAssetID; - -				if (!(sound_id.isNull() -					  || self.mLoadingAssets.find(sound_id) == self.mLoadingAssets.end())) -				{ -					return true; -				} -				break; -			} -		case STEP_CHAT: -		case STEP_WAIT: -		case STEP_EOF: -			{ -				break; -			} -		default: -			{ -				LL_WARNS() << "Unknown gesture step type: " << step->getType() << LL_ENDL; -			} -		} -	} - -	return false; -} - -void LLGestureMgr::stopGesture(LLMultiGesture* gesture) -{ -	if (!gesture) return; - -	// Stop any animations that this gesture is currently playing -	std::set<LLUUID>::const_iterator set_it; -	for (set_it = gesture->mRequestedAnimIDs.begin(); set_it != gesture->mRequestedAnimIDs.end(); ++set_it) -	{ -		const LLUUID& anim_id = *set_it; -		gAgent.sendAnimationRequest(anim_id, ANIM_REQUEST_STOP); -	} -	for (set_it = gesture->mPlayingAnimIDs.begin(); set_it != gesture->mPlayingAnimIDs.end(); ++set_it) -	{ -		const LLUUID& anim_id = *set_it; -		gAgent.sendAnimationRequest(anim_id, ANIM_REQUEST_STOP); -	} - -	std::vector<LLMultiGesture*>::iterator it; -	it = std::find(mPlaying.begin(), mPlaying.end(), gesture); -	while (it != mPlaying.end()) -	{ -		mPlaying.erase(it); -		it = std::find(mPlaying.begin(), mPlaying.end(), gesture); -	} - -	gesture->reset(); - -	if (gesture->mDoneCallback) -	{ -		gesture->mDoneCallback(gesture, gesture->mCallbackData); - -		// callback might have deleted gesture, can't -		// rely on this pointer any more -		gesture = NULL; -	} - -	notifyObservers(); -} - - -void LLGestureMgr::stopGesture(const LLUUID& item_id) -{ -	const LLUUID& base_item_id = gInventory.getLinkedItemID(item_id); - -	item_map_t::iterator it = mActive.find(base_item_id); -	if (it == mActive.end()) return; - -	LLMultiGesture* gesture = (*it).second; -	if (!gesture) return; - -	stopGesture(gesture); -} - - -void LLGestureMgr::addObserver(LLGestureManagerObserver* observer) -{ -	mObservers.push_back(observer); -} - -void LLGestureMgr::removeObserver(LLGestureManagerObserver* observer) -{ -	std::vector<LLGestureManagerObserver*>::iterator it; -	it = std::find(mObservers.begin(), mObservers.end(), observer); -	if (it != mObservers.end()) -	{ -		mObservers.erase(it); -	} -} - -// Call this method when it's time to update everyone on a new state. -// Copy the list because an observer could respond by removing itself -// from the list. -void LLGestureMgr::notifyObservers() -{ -	LL_DEBUGS() << "LLGestureMgr::notifyObservers" << LL_ENDL; - -	for(std::vector<LLGestureManagerObserver*>::iterator iter = mObservers.begin();  -		iter != mObservers.end();  -		++iter) -	{ -		LLGestureManagerObserver* observer = (*iter); -		observer->changed(); -	} -} - -bool LLGestureMgr::matchPrefix(const std::string& in_str, std::string* out_str) -{ -	S32 in_len = in_str.length(); - -	//return whole trigger, if received text equals to it -	item_map_t::iterator it; -	for (it = mActive.begin(); it != mActive.end(); ++it) -	{ -		LLMultiGesture* gesture = (*it).second; -		if (gesture) -		{ -			const std::string& trigger = gesture->getTrigger(); -			if (!LLStringUtil::compareInsensitive(in_str, trigger)) -			{ -				*out_str = trigger; -				return true; -			} -		} -	} - -	//return common chars, if more than one trigger matches the prefix -	std::string rest_of_match = ""; -	std::string buf = ""; -	for (it = mActive.begin(); it != mActive.end(); ++it) -	{ -		LLMultiGesture* gesture = (*it).second; -		if (gesture) -		{ -			const std::string& trigger = gesture->getTrigger(); - -			if (in_len > (S32)trigger.length()) -			{ -				// too short, bail out -				continue; -			} - -			std::string trigger_trunc = trigger; -			LLStringUtil::truncate(trigger_trunc, in_len); -			if (!LLStringUtil::compareInsensitive(in_str, trigger_trunc)) -			{ -				if (rest_of_match.compare("") == 0) -				{ -					rest_of_match = trigger.substr(in_str.size()); -				} -				std::string cur_rest_of_match = trigger.substr(in_str.size()); -				buf = ""; -				S32 i=0; - -				while (i<rest_of_match.length() && i<cur_rest_of_match.length()) -				{ -					if (rest_of_match[i]==cur_rest_of_match[i]) -				    { -						buf.push_back(rest_of_match[i]); -				    } -				    else -				    { -				    	if(i==0) -				    	{ -				    		rest_of_match = ""; -				    	} -				    	break; -				    } -					i++; -				} -				if (rest_of_match.compare("") == 0) -				{ -					return true; -				} -				if (buf.compare("") != 0) -				{ -					rest_of_match = buf; -				} - -			} -		} -	} - -	if (rest_of_match.compare("") != 0) -	{ -		*out_str = in_str+rest_of_match; -		return true; -	} - -	return false; -} - - -void LLGestureMgr::getItemIDs(uuid_vec_t* ids) -{ -	item_map_t::const_iterator it; -	for (it = mActive.begin(); it != mActive.end(); ++it) -	{ -		ids->push_back(it->first); -	} -} - -void LLGestureMgr::done() -{ -	bool notify = false; -	for(item_map_t::iterator it = mActive.begin(); it != mActive.end(); ++it) -	{ -		if(it->second && it->second->mName.empty()) -		{ -			LLViewerInventoryItem* item = gInventory.getItem(it->first); -			if(item) -			{ -				it->second->mName = item->getName(); -				notify = true; -			} -		} -	} -	if(notify) -	{ -		notifyObservers(); -	} -} - - +/**
 + * @file llgesturemgr.cpp
 + * @brief Manager for playing gestures on the viewer
 + *
 + * $LicenseInfo:firstyear=2004&license=viewerlgpl$
 + * Second Life Viewer Source Code
 + * Copyright (C) 2010, Linden Research, Inc.
 + *
 + * This library is free software; you can redistribute it and/or
 + * modify it under the terms of the GNU Lesser General Public
 + * License as published by the Free Software Foundation;
 + * version 2.1 of the License only.
 + *
 + * This library is distributed in the hope that it will be useful,
 + * but WITHOUT ANY WARRANTY; without even the implied warranty of
 + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
 + * Lesser General Public License for more details.
 + *
 + * You should have received a copy of the GNU Lesser General Public
 + * License along with this library; if not, write to the Free Software
 + * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA  02110-1301  USA
 + *
 + * Linden Research, Inc., 945 Battery Street, San Francisco, CA  94111  USA
 + * $/LicenseInfo$
 + */
 +
 +#include "llviewerprecompiledheaders.h"
 +
 +#include "llgesturemgr.h"
 +
 +// system
 +#include <functional>
 +#include <algorithm>
 +
 +// library
 +#include "llaudioengine.h"
 +#include "lldatapacker.h"
 +#include "llfloaterreg.h"
 +#include "llinventory.h"
 +#include "llkeyframemotion.h"
 +#include "llmultigesture.h"
 +#include "llnotificationsutil.h"
 +#include "llstl.h"
 +#include "llstring.h"   // todo: remove
 +#include "llfilesystem.h"
 +#include "message.h"
 +
 +// newview
 +#include "llagent.h"
 +#include "lldelayedgestureerror.h"
 +#include "llinventorymodel.h"
 +#include "llviewermessage.h"
 +#include "llvoavatarself.h"
 +#include "llviewerstats.h"
 +#include "llfloaterimnearbychat.h"
 +#include "llappearancemgr.h"
 +#include "llgesturelistener.h"
 +
 +// Longest time, in seconds, to wait for all animations to stop playing
 +const F32 MAX_WAIT_ANIM_SECS = 30.f;
 +// Longest time, in seconds, to wait for a key release.
 +// This should be relatively long, but not too long. 10 minutes is enough
 +const F32 MAX_WAIT_KEY_SECS = 60.f * 10.f;
 +
 +// Lightweight constructor.
 +// init() does the heavy lifting.
 +LLGestureMgr::LLGestureMgr()
 +:   mValid(false),
 +    mPlaying(),
 +    mActive(),
 +    mLoadingCount(0)
 +{
 +    gInventory.addObserver(this);
 +    mListener.reset(new LLGestureListener());
 +}
 +
 +
 +// We own the data for gestures, so clean them up.
 +LLGestureMgr::~LLGestureMgr()
 +{
 +    item_map_t::iterator it;
 +    for (it = mActive.begin(); it != mActive.end(); ++it)
 +    {
 +        LLMultiGesture* gesture = (*it).second;
 +
 +        delete gesture;
 +        gesture = NULL;
 +    }
 +    gInventory.removeObserver(this);
 +}
 +
 +
 +void LLGestureMgr::init()
 +{
 +    // TODO
 +}
 +
 +void LLGestureMgr::changed(U32 mask)
 +{
 +    LLInventoryFetchItemsObserver::changed(mask);
 +
 +    if (mask & LLInventoryObserver::GESTURE)
 +    {
 +        // If there was a gesture label changed, update all the names in the
 +        // active gestures and then notify observers
 +        if (mask & LLInventoryObserver::LABEL)
 +        {
 +            for(item_map_t::iterator it = mActive.begin(); it != mActive.end(); ++it)
 +            {
 +                if(it->second)
 +                {
 +                    LLViewerInventoryItem* item = gInventory.getItem(it->first);
 +                    if(item)
 +                    {
 +                        it->second->mName = item->getName();
 +                    }
 +                }
 +            }
 +            notifyObservers();
 +        }
 +        // If there was a gesture added or removed notify observers
 +        // STRUCTURE denotes that the inventory item has been moved
 +        // In the case of deleting gesture, it is moved to the trash
 +        else if(mask & LLInventoryObserver::ADD ||
 +                mask & LLInventoryObserver::REMOVE ||
 +                mask & LLInventoryObserver::STRUCTURE)
 +        {
 +            notifyObservers();
 +        }
 +    }
 +}
 +
 +
 +// Use this version when you have the item_id but not the asset_id,
 +// and you KNOW the inventory is loaded.
 +void LLGestureMgr::activateGesture(const LLUUID& item_id)
 +{
 +    LLViewerInventoryItem* item = gInventory.getItem(item_id);
 +    if (!item) return;
 +    if (item->getType() != LLAssetType::AT_GESTURE)
 +        return;
 +
 +    LLUUID asset_id = item->getAssetUUID();
 +
 +    mLoadingCount = 1;
 +    mDeactivateSimilarNames.clear();
 +
 +    const bool inform_server = true;
 +    const bool deactivate_similar = false;
 +    activateGestureWithAsset(item_id, asset_id, inform_server, deactivate_similar);
 +}
 +
 +
 +void LLGestureMgr::activateGestures(LLViewerInventoryItem::item_array_t& items)
 +{
 +    // Load up the assets
 +    S32 count = 0;
 +    LLViewerInventoryItem::item_array_t::const_iterator it;
 +    for (it = items.begin(); it != items.end(); ++it)
 +    {
 +        LLViewerInventoryItem* item = *it;
 +
 +        if (isGestureActive(item->getUUID()))
 +        {
 +            continue;
 +        }
 +        else
 +        { // Make gesture active and persistent through login sessions.  -Aura 07-12-06
 +            activateGesture(item->getUUID());
 +        }
 +
 +        count++;
 +    }
 +
 +    mLoadingCount = count;
 +    mDeactivateSimilarNames.clear();
 +
 +    for (it = items.begin(); it != items.end(); ++it)
 +    {
 +        LLViewerInventoryItem* item = *it;
 +
 +        if (isGestureActive(item->getUUID()))
 +        {
 +            continue;
 +        }
 +
 +        // Don't inform server, we'll do that in bulk
 +        const bool no_inform_server = false;
 +        const bool deactivate_similar = true;
 +        activateGestureWithAsset(item->getUUID(), item->getAssetUUID(),
 +                                 no_inform_server,
 +                                 deactivate_similar);
 +    }
 +
 +    // Inform the database of this change
 +    LLMessageSystem* msg = gMessageSystem;
 +
 +    bool start_message = true;
 +
 +    for (it = items.begin(); it != items.end(); ++it)
 +    {
 +        LLViewerInventoryItem* item = *it;
 +
 +        if (isGestureActive(item->getUUID()))
 +        {
 +            continue;
 +        }
 +
 +        if (start_message)
 +        {
 +            msg->newMessage("ActivateGestures");
 +            msg->nextBlock("AgentData");
 +            msg->addUUID("AgentID", gAgent.getID());
 +            msg->addUUID("SessionID", gAgent.getSessionID());
 +            msg->addU32("Flags", 0x0);
 +            start_message = false;
 +        }
 +
 +        msg->nextBlock("Data");
 +        msg->addUUID("ItemID", item->getUUID());
 +        msg->addUUID("AssetID", item->getAssetUUID());
 +        msg->addU32("GestureFlags", 0x0);
 +
 +        if (msg->getCurrentSendTotal() > MTUBYTES)
 +        {
 +            gAgent.sendReliableMessage();
 +            start_message = true;
 +        }
 +    }
 +
 +    if (!start_message)
 +    {
 +        gAgent.sendReliableMessage();
 +    }
 +}
 +
 +
 +struct LLLoadInfo
 +{
 +    LLUUID mItemID;
 +    bool mInformServer;
 +    bool mDeactivateSimilar;
 +};
 +
 +// If inform_server is true, will send a message upstream to update
 +// the user_gesture_active table.
 +/**
 + * It will load a gesture from remote storage
 + */
 +void LLGestureMgr::activateGestureWithAsset(const LLUUID& item_id,
 +                                                const LLUUID& asset_id,
 +                                                bool inform_server,
 +                                                bool deactivate_similar)
 +{
 +    const LLUUID& base_item_id = gInventory.getLinkedItemID(item_id);
 +
 +    if( !gAssetStorage )
 +    {
 +        LL_WARNS() << "LLGestureMgr::activateGestureWithAsset without valid gAssetStorage" << LL_ENDL;
 +        return;
 +    }
 +    // If gesture is already active, nothing to do.
 +    if (isGestureActive(item_id))
 +    {
 +        LL_WARNS() << "Tried to loadGesture twice " << item_id << LL_ENDL;
 +        return;
 +    }
 +
 +//  if (asset_id.isNull())
 +//  {
 +//      LL_WARNS() << "loadGesture() - gesture has no asset" << LL_ENDL;
 +//      return;
 +//  }
 +
 +    // For now, put NULL into the item map.  We'll build a gesture
 +    // class object when the asset data arrives.
 +    mActive[base_item_id] = NULL;
 +
 +    // Copy the UUID
 +    if (asset_id.notNull())
 +    {
 +        LLLoadInfo* info = new LLLoadInfo;
 +        info->mItemID = base_item_id;
 +        info->mInformServer = inform_server;
 +        info->mDeactivateSimilar = deactivate_similar;
 +
 +        const bool high_priority = true;
 +        gAssetStorage->getAssetData(asset_id,
 +                                    LLAssetType::AT_GESTURE,
 +                                    onLoadComplete,
 +                                    (void*)info,
 +                                    high_priority);
 +    }
 +    else
 +    {
 +        notifyObservers();
 +    }
 +}
 +
 +
 +void notify_update_label(const LLUUID& base_item_id)
 +{
 +    gInventory.addChangedMask(LLInventoryObserver::LABEL, base_item_id);
 +    LLGestureMgr::instance().notifyObservers();
 +}
 +
 +void LLGestureMgr::deactivateGesture(const LLUUID& item_id)
 +{
 +    const LLUUID& base_item_id = gInventory.getLinkedItemID(item_id);
 +    item_map_t::iterator it = mActive.find(base_item_id);
 +    if (it == mActive.end())
 +    {
 +        LL_WARNS() << "deactivateGesture for inactive gesture " << item_id << LL_ENDL;
 +        return;
 +    }
 +
 +    // mActive owns this gesture pointer, so clean up memory.
 +    LLMultiGesture* gesture = (*it).second;
 +
 +    // Can be NULL gestures in the map
 +    if (gesture)
 +    {
 +        stopGesture(gesture);
 +
 +        delete gesture;
 +        gesture = NULL;
 +    }
 +
 +    mActive.erase(it);
 +
 +    // Inform the database of this change
 +    LLMessageSystem* msg = gMessageSystem;
 +    msg->newMessage("DeactivateGestures");
 +    msg->nextBlock("AgentData");
 +    msg->addUUID("AgentID", gAgent.getID());
 +    msg->addUUID("SessionID", gAgent.getSessionID());
 +    msg->addU32("Flags", 0x0);
 +
 +    msg->nextBlock("Data");
 +    msg->addUUID("ItemID", item_id);
 +    msg->addU32("GestureFlags", 0x0);
 +
 +    gAgent.sendReliableMessage();
 +
 +    LLPointer<LLInventoryCallback> cb =
 +        new LLBoostFuncInventoryCallback(no_op_inventory_func,
 +                                         boost::bind(notify_update_label,base_item_id));
 +
 +    LLAppearanceMgr::instance().removeCOFItemLinks(base_item_id, cb);
 +}
 +
 +
 +void LLGestureMgr::deactivateSimilarGestures(LLMultiGesture* in, const LLUUID& in_item_id)
 +{
 +    const LLUUID& base_in_item_id = gInventory.getLinkedItemID(in_item_id);
 +    uuid_vec_t gest_item_ids;
 +
 +    // Deactivate all gestures that match
 +    item_map_t::iterator it;
 +    for (it = mActive.begin(); it != mActive.end(); )
 +    {
 +        const LLUUID& item_id = (*it).first;
 +        LLMultiGesture* gest = (*it).second;
 +
 +        // Don't deactivate the gesture we are looking for duplicates of
 +        // (for replaceGesture)
 +        if (!gest || item_id == base_in_item_id)
 +        {
 +            // legal, can have null pointers in list
 +            ++it;
 +        }
 +        else if ((!gest->mTrigger.empty() && gest->mTrigger == in->mTrigger)
 +                 || (gest->mKey != KEY_NONE && gest->mKey == in->mKey && gest->mMask == in->mMask))
 +        {
 +            gest_item_ids.push_back(item_id);
 +
 +            stopGesture(gest);
 +
 +            delete gest;
 +            gest = NULL;
 +
 +            mActive.erase(it++);
 +            gInventory.addChangedMask(LLInventoryObserver::LABEL, item_id);
 +
 +        }
 +        else
 +        {
 +            ++it;
 +        }
 +    }
 +
 +    // Inform database of the change
 +    LLMessageSystem* msg = gMessageSystem;
 +    bool start_message = true;
 +    uuid_vec_t::const_iterator vit = gest_item_ids.begin();
 +    while (vit != gest_item_ids.end())
 +    {
 +        if (start_message)
 +        {
 +            msg->newMessage("DeactivateGestures");
 +            msg->nextBlock("AgentData");
 +            msg->addUUID("AgentID", gAgent.getID());
 +            msg->addUUID("SessionID", gAgent.getSessionID());
 +            msg->addU32("Flags", 0x0);
 +            start_message = false;
 +        }
 +
 +        msg->nextBlock("Data");
 +        msg->addUUID("ItemID", *vit);
 +        msg->addU32("GestureFlags", 0x0);
 +
 +        if (msg->getCurrentSendTotal() > MTUBYTES)
 +        {
 +            gAgent.sendReliableMessage();
 +            start_message = true;
 +        }
 +
 +        ++vit;
 +    }
 +
 +    if (!start_message)
 +    {
 +        gAgent.sendReliableMessage();
 +    }
 +
 +    // Add to the list of names for the user.
 +    for (vit = gest_item_ids.begin(); vit != gest_item_ids.end(); ++vit)
 +    {
 +        LLViewerInventoryItem* item = gInventory.getItem(*vit);
 +        if (!item) continue;
 +
 +        mDeactivateSimilarNames.append(item->getName());
 +        mDeactivateSimilarNames.append("\n");
 +    }
 +
 +    notifyObservers();
 +}
 +
 +
 +bool LLGestureMgr::isGestureActive(const LLUUID& item_id)
 +{
 +    const LLUUID& base_item_id = gInventory.getLinkedItemID(item_id);
 +    item_map_t::iterator it = mActive.find(base_item_id);
 +    return (it != mActive.end());
 +}
 +
 +
 +bool LLGestureMgr::isGesturePlaying(const LLUUID& item_id)
 +{
 +    const LLUUID& base_item_id = gInventory.getLinkedItemID(item_id);
 +
 +    item_map_t::iterator it = mActive.find(base_item_id);
 +    if (it == mActive.end()) return false;
 +
 +    LLMultiGesture* gesture = (*it).second;
 +    if (!gesture) return false;
 +
 +    return gesture->mPlaying;
 +}
 +
 +bool LLGestureMgr::isGesturePlaying(LLMultiGesture* gesture)
 +{
 +    if(!gesture)
 +    {
 +        return false;
 +    }
 +
 +    return gesture->mPlaying;
 +}
 +
 +void LLGestureMgr::replaceGesture(const LLUUID& item_id, LLMultiGesture* new_gesture, const LLUUID& asset_id)
 +{
 +    const LLUUID& base_item_id = gInventory.getLinkedItemID(item_id);
 +
 +    item_map_t::iterator it = mActive.find(base_item_id);
 +    if (it == mActive.end())
 +    {
 +        LL_WARNS() << "replaceGesture for inactive gesture " << base_item_id << LL_ENDL;
 +        return;
 +    }
 +
 +    LLMultiGesture* old_gesture = (*it).second;
 +    stopGesture(old_gesture);
 +
 +    mActive.erase(base_item_id);
 +
 +    mActive[base_item_id] = new_gesture;
 +
 +    // replaceGesture(const LLUUID& item_id, const LLUUID& new_asset_id)
 +    // replaces ids without repalcing gesture
 +    if (old_gesture != new_gesture)
 +    {
 +        delete old_gesture;
 +        old_gesture = NULL;
 +    }
 +
 +    if (asset_id.notNull())
 +    {
 +        mLoadingCount = 1;
 +        mDeactivateSimilarNames.clear();
 +
 +        LLLoadInfo* info = new LLLoadInfo;
 +        info->mItemID = base_item_id;
 +        info->mInformServer = true;
 +        info->mDeactivateSimilar = false;
 +
 +        const bool high_priority = true;
 +        gAssetStorage->getAssetData(asset_id,
 +                                    LLAssetType::AT_GESTURE,
 +                                    onLoadComplete,
 +                                    (void*)info,
 +                                    high_priority);
 +    }
 +
 +    notifyObservers();
 +}
 +
 +void LLGestureMgr::replaceGesture(const LLUUID& item_id, const LLUUID& new_asset_id)
 +{
 +    const LLUUID& base_item_id = gInventory.getLinkedItemID(item_id);
 +
 +    item_map_t::iterator it = LLGestureMgr::instance().mActive.find(base_item_id);
 +    if (it == mActive.end())
 +    {
 +        LL_WARNS() << "replaceGesture for inactive gesture " << base_item_id << LL_ENDL;
 +        return;
 +    }
 +
 +    // mActive owns this gesture pointer, so clean up memory.
 +    LLMultiGesture* gesture = (*it).second;
 +    LLGestureMgr::instance().replaceGesture(base_item_id, gesture, new_asset_id);
 +}
 +
 +void LLGestureMgr::playGesture(LLMultiGesture* gesture, bool fromKeyPress)
 +{
 +    if (!gesture) return;
 +
 +    // Reset gesture to first step
 +    gesture->mCurrentStep = 0;
 +    gesture->mTriggeredByKey = fromKeyPress;
 +
 +    // Add to list of playing
 +    gesture->mPlaying = true;
 +    mPlaying.push_back(gesture);
 +
 +    // Load all needed assets to minimize the delays
 +    // when gesture is playing.
 +    for (std::vector<LLGestureStep*>::iterator steps_it = gesture->mSteps.begin();
 +         steps_it != gesture->mSteps.end();
 +         ++steps_it)
 +    {
 +        LLGestureStep* step = *steps_it;
 +        switch(step->getType())
 +        {
 +        case STEP_ANIMATION:
 +            {
 +                LLGestureStepAnimation* anim_step = (LLGestureStepAnimation*)step;
 +                const LLUUID& anim_id = anim_step->mAnimAssetID;
 +
 +                // Don't request the animation if this step stops it or if it is already in the cache
 +                if (!(anim_id.isNull()
 +                      || anim_step->mFlags & ANIM_FLAG_STOP
 +                      || gAssetStorage->hasLocalAsset(anim_id, LLAssetType::AT_ANIMATION)))
 +                {
 +                    mLoadingAssets.insert(anim_id);
 +
 +                    LLUUID* id = new LLUUID(gAgentID);
 +                    gAssetStorage->getAssetData(anim_id,
 +                                    LLAssetType::AT_ANIMATION,
 +                                    onAssetLoadComplete,
 +                                    (void *)id,
 +                                    true);
 +                }
 +                break;
 +            }
 +        case STEP_SOUND:
 +            {
 +                LLGestureStepSound* sound_step = (LLGestureStepSound*)step;
 +                const LLUUID& sound_id = sound_step->mSoundAssetID;
 +                if (!(sound_id.isNull()
 +                      || gAssetStorage->hasLocalAsset(sound_id, LLAssetType::AT_SOUND)))
 +                {
 +                    mLoadingAssets.insert(sound_id);
 +
 +                    gAssetStorage->getAssetData(sound_id,
 +                                    LLAssetType::AT_SOUND,
 +                                    onAssetLoadComplete,
 +                                    NULL,
 +                                    true);
 +                }
 +                break;
 +            }
 +        case STEP_CHAT:
 +        case STEP_WAIT:
 +        case STEP_EOF:
 +            {
 +                break;
 +            }
 +        default:
 +            {
 +                LL_WARNS() << "Unknown gesture step type: " << step->getType() << LL_ENDL;
 +            }
 +        }
 +    }
 +
 +    // And get it going
 +    stepGesture(gesture);
 +
 +    notifyObservers();
 +}
 +
 +
 +// Convenience function that looks up the item_id for you.
 +void LLGestureMgr::playGesture(const LLUUID& item_id)
 +{
 +    const LLUUID& base_item_id = gInventory.getLinkedItemID(item_id);
 +
 +    item_map_t::iterator it = mActive.find(base_item_id);
 +    if (it == mActive.end()) return;
 +
 +    LLMultiGesture* gesture = (*it).second;
 +    if (!gesture) return;
 +
 +    playGesture(gesture);
 +}
 +
 +
 +// Iterates through space delimited tokens in string, triggering any gestures found.
 +// Generates a revised string that has the found tokens replaced by their replacement strings
 +// and (as a minor side effect) has multiple spaces in a row replaced by single spaces.
 +bool LLGestureMgr::triggerAndReviseString(const std::string &utf8str, std::string* revised_string)
 +{
 +    std::string tokenized = utf8str;
 +
 +    bool found_gestures = false;
 +    bool first_token = true;
 +
 +    typedef boost::tokenizer<boost::char_separator<char> > tokenizer;
 +    boost::char_separator<char> sep(" ");
 +    tokenizer tokens(tokenized, sep);
 +    tokenizer::iterator token_iter;
 +
 +    for( token_iter = tokens.begin(); token_iter != tokens.end(); ++token_iter)
 +    {
 +        const char* cur_token = token_iter->c_str();
 +        LLMultiGesture* gesture = NULL;
 +
 +        // Only pay attention to the first gesture in the string.
 +        if( !found_gestures )
 +        {
 +            // collect gestures that match
 +            std::vector <LLMultiGesture *> matching;
 +            item_map_t::iterator it;
 +            for (it = mActive.begin(); it != mActive.end(); ++it)
 +            {
 +                gesture = (*it).second;
 +
 +                // Gesture asset data might not have arrived yet
 +                if (!gesture) continue;
 +
 +                if (LLStringUtil::compareInsensitive(gesture->mTrigger, cur_token) == 0)
 +                {
 +                    matching.push_back(gesture);
 +                }
 +
 +                gesture = NULL;
 +            }
 +
 +
 +            if (matching.size() > 0)
 +            {
 +                // choose one at random
 +                {
 +                    S32 random = ll_rand(matching.size());
 +
 +                    gesture = matching[random];
 +
 +                    playGesture(gesture);
 +
 +                    if (!gesture->mReplaceText.empty())
 +                    {
 +                        if( !first_token )
 +                        {
 +                            if (revised_string)
 +                                revised_string->append( " " );
 +                        }
 +
 +                        // Don't muck with the user's capitalization if we don't have to.
 +                        if( LLStringUtil::compareInsensitive(cur_token, gesture->mReplaceText) == 0)
 +                        {
 +                            if (revised_string)
 +                                revised_string->append( cur_token );
 +                        }
 +                        else
 +                        {
 +                            if (revised_string)
 +                                revised_string->append( gesture->mReplaceText );
 +                        }
 +                    }
 +                    found_gestures = true;
 +                }
 +            }
 +        }
 +
 +        if(!gesture)
 +        {
 +            // This token doesn't match a gesture.  Pass it through to the output.
 +            if( !first_token )
 +            {
 +                if (revised_string)
 +                    revised_string->append( " " );
 +            }
 +            if (revised_string)
 +                revised_string->append( cur_token );
 +        }
 +
 +        first_token = false;
 +        gesture = NULL;
 +    }
 +    return found_gestures;
 +}
 +
 +
 +bool LLGestureMgr::triggerGesture(KEY key, MASK mask)
 +{
 +    std::vector <LLMultiGesture *> matching;
 +    item_map_t::iterator it;
 +
 +    // collect matching gestures
 +    for (it = mActive.begin(); it != mActive.end(); ++it)
 +    {
 +        LLMultiGesture* gesture = (*it).second;
 +
 +        // asset data might not have arrived yet
 +        if (!gesture) continue;
 +
 +        if (gesture->mKey == key
 +            && gesture->mMask == mask
 +            && gesture->mWaitingKeyRelease == false)
 +        {
 +            matching.push_back(gesture);
 +        }
 +    }
 +
 +    // choose one and play it
 +    if (matching.size() > 0)
 +    {
 +        U32 random = ll_rand(matching.size());
 +
 +        LLMultiGesture* gesture = matching[random];
 +
 +        playGesture(gesture, true);
 +        return true;
 +    }
 +    return false;
 +}
 +
 +
 +bool LLGestureMgr::triggerGestureRelease(KEY key, MASK mask)
 +{
 +    std::vector <LLMultiGesture *> matching;
 +    item_map_t::iterator it;
 +
 +    // collect matching gestures
 +    for (it = mActive.begin(); it != mActive.end(); ++it)
 +    {
 +        LLMultiGesture* gesture = (*it).second;
 +
 +        // asset data might not have arrived yet
 +        if (!gesture) continue;
 +
 +        if (gesture->mKey == key
 +            && gesture->mMask == mask)
 +        {
 +            gesture->mKeyReleased = true;
 +        }
 +    }
 +
 +    //If we found one, block. Otherwise tell them it's free to go.
 +    return matching.size() > 0;
 +}
 +
 +
 +S32 LLGestureMgr::getPlayingCount() const
 +{
 +    return mPlaying.size();
 +}
 +
 +
 +struct IsGesturePlaying
 +{
 +    bool operator()(const LLMultiGesture* gesture) const
 +    {
 +        return bool(gesture->mPlaying);
 +    }
 +};
 +
 +void LLGestureMgr::update()
 +{
 +    S32 i;
 +    for (i = 0; i < (S32)mPlaying.size(); ++i)
 +    {
 +        stepGesture(mPlaying[i]);
 +    }
 +
 +    // Clear out gestures that are done, by moving all the
 +    // ones that are still playing to the front.
 +    std::vector<LLMultiGesture*>::iterator new_end;
 +    new_end = std::partition(mPlaying.begin(),
 +                             mPlaying.end(),
 +                             IsGesturePlaying());
 +
 +    // Something finished playing
 +    if (new_end != mPlaying.end())
 +    {
 +        // Delete the completed gestures that want deletion
 +        std::vector<LLMultiGesture*>::iterator it;
 +        for (it = new_end; it != mPlaying.end(); ++it)
 +        {
 +            LLMultiGesture* gesture = *it;
 +
 +            if (gesture->mDoneCallback)
 +            {
 +                gesture->mDoneCallback(gesture, gesture->mCallbackData);
 +
 +                // callback might have deleted gesture, can't
 +                // rely on this pointer any more
 +                gesture = NULL;
 +            }
 +        }
 +
 +        // And take done gestures out of the playing list
 +        mPlaying.erase(new_end, mPlaying.end());
 +
 +        notifyObservers();
 +    }
 +}
 +
 +
 +// Run all steps until you're either done or hit a wait.
 +void LLGestureMgr::stepGesture(LLMultiGesture* gesture)
 +{
 +    if (!gesture)
 +    {
 +        return;
 +    }
 +    if (!isAgentAvatarValid() || hasLoadingAssets(gesture)) return;
 +
 +    // Of the ones that started playing, have any stopped?
 +
 +    std::set<LLUUID>::iterator gest_it;
 +    for (gest_it = gesture->mPlayingAnimIDs.begin();
 +         gest_it != gesture->mPlayingAnimIDs.end();
 +         )
 +    {
 +        // look in signaled animations (simulator's view of what is
 +        // currently playing.
 +        LLVOAvatar::AnimIterator play_it = gAgentAvatarp->mSignaledAnimations.find(*gest_it);
 +        if (play_it != gAgentAvatarp->mSignaledAnimations.end())
 +        {
 +            ++gest_it;
 +        }
 +        else
 +        {
 +            // not found, so not currently playing or scheduled to play
 +            // delete from the triggered set
 +            gesture->mPlayingAnimIDs.erase(gest_it++);
 +        }
 +    }
 +
 +    // Of all the animations that we asked the sim to start for us,
 +    // pick up the ones that have actually started.
 +    for (gest_it = gesture->mRequestedAnimIDs.begin();
 +         gest_it != gesture->mRequestedAnimIDs.end();
 +         )
 +    {
 +     LLVOAvatar::AnimIterator play_it = gAgentAvatarp->mSignaledAnimations.find(*gest_it);
 +        if (play_it != gAgentAvatarp->mSignaledAnimations.end())
 +        {
 +            // Hooray, this animation has started playing!
 +            // Copy into playing.
 +            gesture->mPlayingAnimIDs.insert(*gest_it);
 +            gesture->mRequestedAnimIDs.erase(gest_it++);
 +        }
 +        else
 +        {
 +            // nope, not playing yet
 +            ++gest_it;
 +        }
 +    }
 +
 +    // Run the current steps
 +    bool waiting = false;
 +    while (!waiting && gesture->mPlaying)
 +    {
 +        // Get the current step, if there is one.
 +        // Otherwise enter the waiting at end state.
 +        LLGestureStep* step = NULL;
 +        if (gesture->mCurrentStep < (S32)gesture->mSteps.size())
 +        {
 +            step = gesture->mSteps[gesture->mCurrentStep];
 +            llassert(step != NULL);
 +        }
 +        else
 +        {
 +            // step stays null, we're off the end
 +            gesture->mWaitingAtEnd = true;
 +        }
 +
 +
 +        // If we're waiting at the end, wait for all gestures to stop
 +        // playing.
 +        // TODO: Wait for all sounds to complete as well.
 +        if (gesture->mWaitingAtEnd)
 +        {
 +            // Neither do we have any pending requests, nor are they
 +            // still playing.
 +            if ((gesture->mRequestedAnimIDs.empty()
 +                && gesture->mPlayingAnimIDs.empty()))
 +            {
 +                // all animations are done playing
 +                gesture->mWaitingAtEnd = false;
 +                gesture->mPlaying = false;
 +            }
 +            else
 +            {
 +                waiting = true;
 +            }
 +            continue;
 +        }
 +
 +        // If we're waiting a fixed amount of time, check for timer
 +        // expiration.
 +        if (gesture->mWaitingKeyRelease)
 +        {
 +            // We're waiting for a certain amount of time to pass
 +            if (gesture->mKeyReleased)
 +            {
 +                // wait is done, continue execution
 +                gesture->mWaitingKeyRelease = false;
 +                gesture->mCurrentStep++;
 +            }
 +            else if (gesture->mWaitTimer.getElapsedTimeF32() > MAX_WAIT_KEY_SECS)
 +            {
 +                LL_INFOS("GestureMgr") << "Waited too long for key release, continuing gesture."
 +                    << LL_ENDL;
 +                gesture->mWaitingKeyRelease = false;
 +                gesture->mCurrentStep++;
 +            }
 +            else
 +            {
 +                // we're waiting, so execution is done for now
 +                waiting = true;
 +            }
 +            continue;
 +        }
 +
 +        // If we're waiting on our animations to stop, poll for
 +        // completion.
 +        if (gesture->mWaitingAnimations)
 +        {
 +            // Neither do we have any pending requests, nor are they
 +            // still playing.
 +            if ((gesture->mRequestedAnimIDs.empty()
 +                && gesture->mPlayingAnimIDs.empty()))
 +            {
 +                // all animations are done playing
 +                gesture->mWaitingAnimations = false;
 +                gesture->mCurrentStep++;
 +            }
 +            else if (gesture->mWaitTimer.getElapsedTimeF32() > MAX_WAIT_ANIM_SECS)
 +            {
 +                // we've waited too long for an animation
 +                LL_INFOS("GestureMgr") << "Waited too long for animations to stop, continuing gesture."
 +                    << LL_ENDL;
 +                gesture->mWaitingAnimations = false;
 +                gesture->mCurrentStep++;
 +            }
 +            else
 +            {
 +                waiting = true;
 +            }
 +            continue;
 +        }
 +
 +        // If we're waiting a fixed amount of time, check for timer
 +        // expiration.
 +        if (gesture->mWaitingTimer)
 +        {
 +            // We're waiting for a certain amount of time to pass
 +            LLGestureStepWait* wait_step = (LLGestureStepWait*)step;
 +
 +            F32 elapsed = gesture->mWaitTimer.getElapsedTimeF32();
 +            if (elapsed > wait_step->mWaitSeconds)
 +            {
 +                // wait is done, continue execution
 +                gesture->mWaitingTimer = false;
 +                gesture->mCurrentStep++;
 +            }
 +            else
 +            {
 +                // we're waiting, so execution is done for now
 +                waiting = true;
 +            }
 +            continue;
 +        }
 +
 +        // Not waiting, do normal execution
 +        runStep(gesture, step);
 +    }
 +}
 +
 +
 +void LLGestureMgr::runStep(LLMultiGesture* gesture, LLGestureStep* step)
 +{
 +    switch(step->getType())
 +    {
 +    case STEP_ANIMATION:
 +        {
 +            LLGestureStepAnimation* anim_step = (LLGestureStepAnimation*)step;
 +            if (anim_step->mAnimAssetID.isNull())
 +            {
 +                gesture->mCurrentStep++;
 +            }
 +
 +            if (anim_step->mFlags & ANIM_FLAG_STOP)
 +            {
 +                gAgent.sendAnimationRequest(anim_step->mAnimAssetID, ANIM_REQUEST_STOP);
 +                // remove it from our request set in case we just requested it
 +                std::set<LLUUID>::iterator set_it = gesture->mRequestedAnimIDs.find(anim_step->mAnimAssetID);
 +                if (set_it != gesture->mRequestedAnimIDs.end())
 +                {
 +                    gesture->mRequestedAnimIDs.erase(set_it);
 +                }
 +            }
 +            else
 +            {
 +                gAgent.sendAnimationRequest(anim_step->mAnimAssetID, ANIM_REQUEST_START);
 +                // Indicate that we've requested this animation to play as
 +                // part of this gesture (but it won't start playing for at
 +                // least one round-trip to simulator).
 +                gesture->mRequestedAnimIDs.insert(anim_step->mAnimAssetID);
 +            }
 +            gesture->mCurrentStep++;
 +            break;
 +        }
 +    case STEP_SOUND:
 +        {
 +            LLGestureStepSound* sound_step = (LLGestureStepSound*)step;
 +            const LLUUID& sound_id = sound_step->mSoundAssetID;
 +            const F32 volume = 1.f;
 +            send_sound_trigger(sound_id, volume);
 +            gesture->mCurrentStep++;
 +            break;
 +        }
 +    case STEP_CHAT:
 +        {
 +            LLGestureStepChat* chat_step = (LLGestureStepChat*)step;
 +            std::string chat_text = chat_step->mChatText;
 +            // Don't animate the nodding, as this might not blend with
 +            // other playing animations.
 +
 +            const bool animate = false;
 +
 +            (LLFloaterReg::getTypedInstance<LLFloaterIMNearbyChat>("nearby_chat"))->
 +                    sendChatFromViewer(chat_text, CHAT_TYPE_NORMAL, animate);
 +
 +            gesture->mCurrentStep++;
 +            break;
 +        }
 +    case STEP_WAIT:
 +        {
 +            LLGestureStepWait* wait_step = (LLGestureStepWait*)step;
 +            if (gesture->mTriggeredByKey // Only wait here IF we were triggered by a key!
 +                && gesture->mWaitingKeyRelease == false // We can only do this once! Prevent gestures infinitely running
 +                && wait_step->mFlags & WAIT_FLAG_KEY_RELEASE)
 +            {
 +                // Lets wait for the key release first so we don't hold up re-presses
 +                gesture->mWaitingKeyRelease = true;
 +                gesture->mKeyReleased = false;
 +                // Use the wait timer as a deadlock breaker for key release waits.
 +                gesture->mWaitTimer.reset();
 +            }
 +            else if (wait_step->mFlags & WAIT_FLAG_TIME)
 +            {
 +                gesture->mWaitingTimer = true;
 +                gesture->mWaitTimer.reset();
 +            }
 +            else if (wait_step->mFlags & WAIT_FLAG_ALL_ANIM)
 +            {
 +                gesture->mWaitingAnimations = true;
 +                // Use the wait timer as a deadlock breaker for animation waits.
 +                gesture->mWaitTimer.reset();
 +            }
 +            else
 +            {
 +                gesture->mCurrentStep++;
 +            }
 +            // Don't increment instruction pointer until wait is complete.
 +            break;
 +        }
 +    default:
 +        {
 +            break;
 +        }
 +    }
 +}
 +
 +
 +// static
 +void LLGestureMgr::onLoadComplete(const LLUUID& asset_uuid,
 +                                  LLAssetType::EType type,
 +                                  void* user_data, S32 status, LLExtStat ext_status)
 +{
 +    LLLoadInfo* info = (LLLoadInfo*)user_data;
 +
 +    LLUUID item_id = info->mItemID;
 +    bool inform_server = info->mInformServer;
 +    bool deactivate_similar = info->mDeactivateSimilar;
 +
 +    delete info;
 +    info = NULL;
 +    LLGestureMgr& self = LLGestureMgr::instance();
 +    self.mLoadingCount--;
 +
 +    if (0 == status)
 +    {
 +        LLFileSystem file(asset_uuid, type, LLFileSystem::READ);
 +        S32 size = file.getSize();
 +
 +        std::vector<char> buffer(size+1);
 +
 +        file.read((U8*)&buffer[0], size);
 +        // ensure there's a trailing NULL so strlen will work.
 +        buffer[size] = '\0';
 +
 +        LLMultiGesture* gesture = new LLMultiGesture();
 +
 +        LLDataPackerAsciiBuffer dp(&buffer[0], size+1);
 +        bool ok = gesture->deserialize(dp);
 +
 +        if (ok)
 +        {
 +            if (deactivate_similar)
 +            {
 +                self.deactivateSimilarGestures(gesture, item_id);
 +
 +                // Display deactivation message if this was the last of the bunch.
 +                if (self.mLoadingCount == 0
 +                    && self.mDeactivateSimilarNames.length() > 0)
 +                {
 +                    // we're done with this set of deactivations
 +                    LLSD args;
 +                    args["NAMES"] = self.mDeactivateSimilarNames;
 +                    LLNotificationsUtil::add("DeactivatedGesturesTrigger", args);
 +                }
 +            }
 +
 +            LLViewerInventoryItem* item = gInventory.getItem(item_id);
 +            if(item)
 +            {
 +                gesture->mName = item->getName();
 +            }
 +            else
 +            {
 +                // Watch this item and set gesture name when item exists in inventory
 +                self.setFetchID(item_id);
 +                self.startFetch();
 +            }
 +
 +            item_map_t::iterator it = self.mActive.find(item_id);
 +            if (it == self.mActive.end())
 +            {
 +                // Gesture is supposed to be present, active, but NULL
 +                LL_DEBUGS("GestureMgr") << "Gesture " << item_id << " not found in active list" << LL_ENDL;
 +            }
 +            else
 +            {
 +                LLMultiGesture* old_gesture = (*it).second;
 +                if (old_gesture && old_gesture != gesture)
 +                {
 +                    LL_DEBUGS("GestureMgr") << "Received dupplicate " << item_id << " callback" << LL_ENDL;
 +                    // In case somebody managest to activate, deactivate and
 +                    // then activate gesture again, before asset finishes loading.
 +                    // LLLoadInfo will have a different pointer, asset storage will
 +                    // see it as a different request, resulting in two callbacks.
 +
 +                    // deactivateSimilarGestures() did not turn this one off
 +                    // because of matching item_id
 +                    self.stopGesture(old_gesture);
 +
 +                    self.mActive.erase(item_id);
 +                    delete old_gesture;
 +                    old_gesture = NULL;
 +                }
 +            }
 +
 +            self.mActive[item_id] = gesture;
 +
 +            // Everything has been successful.  Add to the active list.
 +            gInventory.addChangedMask(LLInventoryObserver::LABEL, item_id);
 +
 +            if (inform_server)
 +            {
 +                // Inform the database of this change
 +                LLMessageSystem* msg = gMessageSystem;
 +                msg->newMessage("ActivateGestures");
 +                msg->nextBlock("AgentData");
 +                msg->addUUID("AgentID", gAgent.getID());
 +                msg->addUUID("SessionID", gAgent.getSessionID());
 +                msg->addU32("Flags", 0x0);
 +
 +                msg->nextBlock("Data");
 +                msg->addUUID("ItemID", item_id);
 +                msg->addUUID("AssetID", asset_uuid);
 +                msg->addU32("GestureFlags", 0x0);
 +
 +                gAgent.sendReliableMessage();
 +            }
 +            callback_map_t::iterator i_cb = self.mCallbackMap.find(item_id);
 +
 +            if(i_cb != self.mCallbackMap.end())
 +            {
 +                i_cb->second(gesture);
 +                self.mCallbackMap.erase(i_cb);
 +            }
 +
 +            self.notifyObservers();
 +        }
 +        else
 +        {
 +            LL_WARNS("GestureMgr") << "Unable to load gesture" << LL_ENDL;
 +
 +            item_map_t::iterator it = self.mActive.find(item_id);
 +            if (it != self.mActive.end())
 +            {
 +                LLMultiGesture* old_gesture = (*it).second;
 +                if (old_gesture)
 +                {
 +                    // Shouldn't happen, just in case
 +                    LL_WARNS("GestureMgr") << "Gesture " << item_id << " existed when it shouldn't" << LL_ENDL;
 +
 +                    self.stopGesture(old_gesture);
 +                    delete old_gesture;
 +                    old_gesture = NULL;
 +                }
 +                self.mActive.erase(item_id);
 +            }
 +
 +            delete gesture;
 +            gesture = NULL;
 +        }
 +    }
 +    else
 +    {
 +        if( LL_ERR_ASSET_REQUEST_NOT_IN_DATABASE == status ||
 +            LL_ERR_FILE_EMPTY == status)
 +        {
 +            LLDelayedGestureError::gestureMissing( item_id );
 +        }
 +        else
 +        {
 +            LLDelayedGestureError::gestureFailedToLoad( item_id );
 +        }
 +
 +        LL_WARNS("GestureMgr") << "Problem loading gesture: " << status << LL_ENDL;
 +
 +        item_map_t::iterator it = self.mActive.find(item_id);
 +        if (it != self.mActive.end())
 +        {
 +            LLMultiGesture* old_gesture = (*it).second;
 +            if (old_gesture)
 +            {
 +                // Shouldn't happen, just in case
 +                LL_WARNS("GestureMgr") << "Gesture " << item_id << " existed when it shouldn't" << LL_ENDL;
 +
 +                self.stopGesture(old_gesture);
 +                delete old_gesture;
 +                old_gesture = NULL;
 +            }
 +            self.mActive.erase(item_id);
 +        }
 +    }
 +}
 +
 +// static
 +void LLGestureMgr::onAssetLoadComplete(const LLUUID& asset_uuid,
 +                                       LLAssetType::EType type,
 +                                       void* user_data, S32 status, LLExtStat ext_status)
 +{
 +    LLGestureMgr& self = LLGestureMgr::instance();
 +
 +    // Complete the asset loading process depending on the type and
 +    // remove the asset id from pending downloads list.
 +    switch(type)
 +    {
 +    case LLAssetType::AT_ANIMATION:
 +        {
 +            LLKeyframeMotion::onLoadComplete(asset_uuid, type, user_data, status, ext_status);
 +
 +            self.mLoadingAssets.erase(asset_uuid);
 +
 +            break;
 +        }
 +    case LLAssetType::AT_SOUND:
 +        {
 +            LLAudioEngine::assetCallback(asset_uuid, type, user_data, status, ext_status);
 +
 +            self.mLoadingAssets.erase(asset_uuid);
 +
 +            break;
 +        }
 +    default:
 +        {
 +            LL_WARNS() << "Unexpected asset type: " << type << LL_ENDL;
 +
 +            // We don't want to return from this callback without
 +            // an animation or sound callback being fired
 +            // and *user_data handled to avoid memory leaks.
 +            llassert(type == LLAssetType::AT_ANIMATION || type == LLAssetType::AT_SOUND);
 +        }
 +    }
 +}
 +
 +// static
 +bool LLGestureMgr::hasLoadingAssets(LLMultiGesture* gesture)
 +{
 +    LLGestureMgr& self = LLGestureMgr::instance();
 +
 +    for (std::vector<LLGestureStep*>::iterator steps_it = gesture->mSteps.begin();
 +         steps_it != gesture->mSteps.end();
 +         ++steps_it)
 +    {
 +        LLGestureStep* step = *steps_it;
 +        switch(step->getType())
 +        {
 +        case STEP_ANIMATION:
 +            {
 +                LLGestureStepAnimation* anim_step = (LLGestureStepAnimation*)step;
 +                const LLUUID& anim_id = anim_step->mAnimAssetID;
 +
 +                if (!(anim_id.isNull()
 +                      || anim_step->mFlags & ANIM_FLAG_STOP
 +                      || self.mLoadingAssets.find(anim_id) == self.mLoadingAssets.end()))
 +                {
 +                    return true;
 +                }
 +                break;
 +            }
 +        case STEP_SOUND:
 +            {
 +                LLGestureStepSound* sound_step = (LLGestureStepSound*)step;
 +                const LLUUID& sound_id = sound_step->mSoundAssetID;
 +
 +                if (!(sound_id.isNull()
 +                      || self.mLoadingAssets.find(sound_id) == self.mLoadingAssets.end()))
 +                {
 +                    return true;
 +                }
 +                break;
 +            }
 +        case STEP_CHAT:
 +        case STEP_WAIT:
 +        case STEP_EOF:
 +            {
 +                break;
 +            }
 +        default:
 +            {
 +                LL_WARNS() << "Unknown gesture step type: " << step->getType() << LL_ENDL;
 +            }
 +        }
 +    }
 +
 +    return false;
 +}
 +
 +void LLGestureMgr::stopGesture(LLMultiGesture* gesture)
 +{
 +    if (!gesture) return;
 +
 +    // Stop any animations that this gesture is currently playing
 +    std::set<LLUUID>::const_iterator set_it;
 +    for (set_it = gesture->mRequestedAnimIDs.begin(); set_it != gesture->mRequestedAnimIDs.end(); ++set_it)
 +    {
 +        const LLUUID& anim_id = *set_it;
 +        gAgent.sendAnimationRequest(anim_id, ANIM_REQUEST_STOP);
 +    }
 +    for (set_it = gesture->mPlayingAnimIDs.begin(); set_it != gesture->mPlayingAnimIDs.end(); ++set_it)
 +    {
 +        const LLUUID& anim_id = *set_it;
 +        gAgent.sendAnimationRequest(anim_id, ANIM_REQUEST_STOP);
 +    }
 +
 +    std::vector<LLMultiGesture*>::iterator it;
 +    it = std::find(mPlaying.begin(), mPlaying.end(), gesture);
 +    while (it != mPlaying.end())
 +    {
 +        mPlaying.erase(it);
 +        it = std::find(mPlaying.begin(), mPlaying.end(), gesture);
 +    }
 +
 +    gesture->reset();
 +
 +    if (gesture->mDoneCallback)
 +    {
 +        gesture->mDoneCallback(gesture, gesture->mCallbackData);
 +
 +        // callback might have deleted gesture, can't
 +        // rely on this pointer any more
 +        gesture = NULL;
 +    }
 +
 +    notifyObservers();
 +}
 +
 +
 +void LLGestureMgr::stopGesture(const LLUUID& item_id)
 +{
 +    const LLUUID& base_item_id = gInventory.getLinkedItemID(item_id);
 +
 +    item_map_t::iterator it = mActive.find(base_item_id);
 +    if (it == mActive.end()) return;
 +
 +    LLMultiGesture* gesture = (*it).second;
 +    if (!gesture) return;
 +
 +    stopGesture(gesture);
 +}
 +
 +
 +void LLGestureMgr::addObserver(LLGestureManagerObserver* observer)
 +{
 +    mObservers.push_back(observer);
 +}
 +
 +void LLGestureMgr::removeObserver(LLGestureManagerObserver* observer)
 +{
 +    std::vector<LLGestureManagerObserver*>::iterator it;
 +    it = std::find(mObservers.begin(), mObservers.end(), observer);
 +    if (it != mObservers.end())
 +    {
 +        mObservers.erase(it);
 +    }
 +}
 +
 +// Call this method when it's time to update everyone on a new state.
 +// Copy the list because an observer could respond by removing itself
 +// from the list.
 +void LLGestureMgr::notifyObservers()
 +{
 +    LL_DEBUGS() << "LLGestureMgr::notifyObservers" << LL_ENDL;
 +
 +    for(std::vector<LLGestureManagerObserver*>::iterator iter = mObservers.begin();
 +        iter != mObservers.end();
 +        ++iter)
 +    {
 +        LLGestureManagerObserver* observer = (*iter);
 +        observer->changed();
 +    }
 +}
 +
 +bool LLGestureMgr::matchPrefix(const std::string& in_str, std::string* out_str)
 +{
 +    S32 in_len = in_str.length();
 +
 +    //return whole trigger, if received text equals to it
 +    item_map_t::iterator it;
 +    for (it = mActive.begin(); it != mActive.end(); ++it)
 +    {
 +        LLMultiGesture* gesture = (*it).second;
 +        if (gesture)
 +        {
 +            const std::string& trigger = gesture->getTrigger();
 +            if (!LLStringUtil::compareInsensitive(in_str, trigger))
 +            {
 +                *out_str = trigger;
 +                return true;
 +            }
 +        }
 +    }
 +
 +    //return common chars, if more than one trigger matches the prefix
 +    std::string rest_of_match = "";
 +    std::string buf = "";
 +    for (it = mActive.begin(); it != mActive.end(); ++it)
 +    {
 +        LLMultiGesture* gesture = (*it).second;
 +        if (gesture)
 +        {
 +            const std::string& trigger = gesture->getTrigger();
 +
 +            if (in_len > (S32)trigger.length())
 +            {
 +                // too short, bail out
 +                continue;
 +            }
 +
 +            std::string trigger_trunc = trigger;
 +            LLStringUtil::truncate(trigger_trunc, in_len);
 +            if (!LLStringUtil::compareInsensitive(in_str, trigger_trunc))
 +            {
 +                if (rest_of_match.compare("") == 0)
 +                {
 +                    rest_of_match = trigger.substr(in_str.size());
 +                }
 +                std::string cur_rest_of_match = trigger.substr(in_str.size());
 +                buf = "";
 +                S32 i=0;
 +
 +                while (i<rest_of_match.length() && i<cur_rest_of_match.length())
 +                {
 +                    if (rest_of_match[i]==cur_rest_of_match[i])
 +                    {
 +                        buf.push_back(rest_of_match[i]);
 +                    }
 +                    else
 +                    {
 +                        if(i==0)
 +                        {
 +                            rest_of_match = "";
 +                        }
 +                        break;
 +                    }
 +                    i++;
 +                }
 +                if (rest_of_match.compare("") == 0)
 +                {
 +                    return true;
 +                }
 +                if (buf.compare("") != 0)
 +                {
 +                    rest_of_match = buf;
 +                }
 +
 +            }
 +        }
 +    }
 +
 +    if (rest_of_match.compare("") != 0)
 +    {
 +        *out_str = in_str+rest_of_match;
 +        return true;
 +    }
 +
 +    return false;
 +}
 +
 +
 +void LLGestureMgr::getItemIDs(uuid_vec_t* ids)
 +{
 +    item_map_t::const_iterator it;
 +    for (it = mActive.begin(); it != mActive.end(); ++it)
 +    {
 +        ids->push_back(it->first);
 +    }
 +}
 +
 +void LLGestureMgr::done()
 +{
 +    bool notify = false;
 +    for(item_map_t::iterator it = mActive.begin(); it != mActive.end(); ++it)
 +    {
 +        if(it->second && it->second->mName.empty())
 +        {
 +            LLViewerInventoryItem* item = gInventory.getItem(it->first);
 +            if(item)
 +            {
 +                it->second->mName = item->getName();
 +                notify = true;
 +            }
 +        }
 +    }
 +    if(notify)
 +    {
 +        notifyObservers();
 +    }
 +}
 +
 +
  | 
