diff options
Diffstat (limited to 'indra/newview/llgesturemgr.cpp')
-rw-r--r-- | indra/newview/llgesturemgr.cpp | 1067 |
1 files changed, 1067 insertions, 0 deletions
diff --git a/indra/newview/llgesturemgr.cpp b/indra/newview/llgesturemgr.cpp new file mode 100644 index 0000000000..b9ae06963f --- /dev/null +++ b/indra/newview/llgesturemgr.cpp @@ -0,0 +1,1067 @@ +/** + * @file llgesturemgr.cpp + * @brief Manager for playing gestures on the viewer + * + * Copyright (c) 2004-$CurrentYear$, Linden Research, Inc. + * $License$ + */ + +#include "llviewerprecompiledheaders.h" + +#include "llgesturemgr.h" + +// system +#include <functional> +#include <algorithm> +#include <boost/tokenizer.hpp> + +// library +#include "lldatapacker.h" +#include "llinventory.h" +#include "llmultigesture.h" +#include "llstl.h" +#include "llstring.h" // todo: remove +#include "llvfile.h" +#include "message.h" + +// newview +#include "llagent.h" +#include "llchatbar.h" +#include "llinventorymodel.h" +#include "llnotify.h" +#include "llviewermessage.h" +#include "llvoavatar.h" +#include "llviewerstats.h" +#include "viewer.h" + +LLGestureManager gGestureManager; + +// 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. +LLGestureManager::LLGestureManager() +: mValid(FALSE), + mPlaying(), + mActive(), + mLoadingCount(0) +{ } + + +// We own the data for gestures, so clean them up. +LLGestureManager::~LLGestureManager() +{ + item_map_t::iterator it; + for (it = mActive.begin(); it != mActive.end(); ++it) + { + LLMultiGesture* gesture = (*it).second; + + delete gesture; + gesture = NULL; + } +} + + +void LLGestureManager::init() +{ + // TODO +} + + +// Use this version when you have the item_id but not the asset_id, +// and you KNOW the inventory is loaded. +void LLGestureManager::activateGesture(const LLUUID& item_id) +{ + LLViewerInventoryItem* item = gInventory.getItem(item_id); + if (!item) return; + + LLUUID asset_id = item->getAssetUUID(); + + mLoadingCount = 1; + mDeactivateSimilarNames.clear(); + + const BOOL inform_server = TRUE; + const BOOL deactivate_similar = TRUE; + activateGestureWithAsset(item_id, asset_id, inform_server, deactivate_similar); +} + + +void LLGestureManager::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. -spatters 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. +void LLGestureManager::activateGestureWithAsset(const LLUUID& item_id, + const LLUUID& asset_id, + BOOL inform_server, + BOOL deactivate_similar) +{ + // If gesture is already active, nothing to do. + if (isGestureActive(item_id)) + { + llwarns << "Tried to loadGesture twice " << item_id << llendl; + return; + } + +// if (asset_id.isNull()) +// { +// llwarns << "loadGesture() - gesture has no asset" << llendl; +// return; +// } + + // For now, put NULL into the item map. We'll build a gesture + // class object when the asset data arrives. + mActive[item_id] = NULL; + + // Copy the UUID + if (asset_id.notNull()) + { + LLLoadInfo* info = new LLLoadInfo; + info->mItemID = 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 LLGestureManager::deactivateGesture(const LLUUID& item_id) +{ + item_map_t::iterator it = mActive.find(item_id); + if (it == mActive.end()) + { + llwarns << "deactivateGesture for inactive gesture " << item_id << llendl; + 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); + gInventory.addChangedMask(LLInventoryObserver::LABEL, item_id); + + // 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(); + + notifyObservers(); +} + + +void LLGestureManager::deactivateSimilarGestures(LLMultiGesture* in, const LLUUID& in_item_id) +{ + std::vector<LLUUID> 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 == 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; + std::vector<LLUUID>::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 LLGestureManager::isGestureActive(const LLUUID& item_id) +{ + item_map_t::iterator it = mActive.find(item_id); + return (it != mActive.end()); +} + + +BOOL LLGestureManager::isGesturePlaying(const LLUUID& item_id) +{ + item_map_t::iterator it = mActive.find(item_id); + if (it == mActive.end()) return FALSE; + + LLMultiGesture* gesture = (*it).second; + if (!gesture) return FALSE; + + return gesture->mPlaying; +} + +void LLGestureManager::replaceGesture(const LLUUID& item_id, LLMultiGesture* new_gesture, const LLUUID& asset_id) +{ + item_map_t::iterator it = mActive.find(item_id); + if (it == mActive.end()) + { + llwarns << "replaceGesture for inactive gesture " << item_id << llendl; + return; + } + + LLMultiGesture* old_gesture = (*it).second; + stopGesture(old_gesture); + + mActive.erase(item_id); + + mActive[item_id] = new_gesture; + + delete old_gesture; + old_gesture = NULL; + + if (asset_id.notNull()) + { + mLoadingCount = 1; + mDeactivateSimilarNames.clear(); + + LLLoadInfo* info = new LLLoadInfo; + info->mItemID = item_id; + info->mInformServer = TRUE; + info->mDeactivateSimilar = TRUE; + + const BOOL high_priority = TRUE; + gAssetStorage->getAssetData(asset_id, + LLAssetType::AT_GESTURE, + onLoadComplete, + (void*)info, + high_priority); + } + + notifyObservers(); +} + + +void LLGestureManager::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); + + // And get it going + stepGesture(gesture); + + notifyObservers(); +} + + +// Convenience function that looks up the item_id for you. +void LLGestureManager::playGesture(const LLUUID& item_id) +{ + item_map_t::iterator it = mActive.find(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 LLGestureManager::triggerAndReviseString(const std::string &utf8str, std::string* revised_string) +{ + LLString tokenized = LLString(utf8str.c_str()); + + 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 ) + { + LLString cur_token_lower = cur_token; + LLString::toLower(cur_token_lower); + + 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 (!stricmp(gesture->mTrigger.c_str(), cur_token_lower.c_str())) + { + playGesture(gesture); + + if (!gesture->mReplaceText.empty()) + { + if( !first_token ) + { + revised_string->append( " " ); + } + + // Don't muck with the user's capitalization if we don't have to. + LLString output = gesture->mReplaceText.c_str(); + LLString output_lower = output; + LLString::toLower(output_lower); + if( cur_token_lower == output_lower ) + { + revised_string->append( cur_token ); + } + else + { + revised_string->append( output ); + } + } + found_gestures = TRUE; + break; + } + gesture = NULL; + } + } + + if( !gesture ) + { + if( !first_token ) + { + revised_string->append( " " ); + } + revised_string->append( cur_token ); + } + + first_token = FALSE; + } + return found_gestures; +} + + +BOOL LLGestureManager::triggerGesture(KEY key, MASK mask) +{ + item_map_t::iterator it; + 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) + { + playGesture(gesture); + return TRUE; + } + } + return FALSE; +} + + +S32 LLGestureManager::getPlayingCount() const +{ + return mPlaying.size(); +} + + +struct IsGesturePlaying : public std::unary_function<LLMultiGesture*, bool> +{ + bool operator()(const LLMultiGesture* gesture) const + { + return gesture->mPlaying ? true : false; + } +}; + +void LLGestureManager::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 LLGestureManager::stepGesture(LLMultiGesture* gesture) +{ + if (!gesture) + { + return; + } + LLVOAvatar* avatar = gAgent.getAvatarObject(); + if (!avatar) 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 = avatar->mSignaledAnimations.find(*gest_it); + if (play_it != avatar->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 = avatar->mSignaledAnimations.find(*gest_it); + if (play_it != avatar->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 + llinfos << "Waited too long for animations to stop, continuing gesture." + << llendl; + 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 LLGestureManager::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; + + gChatBar->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 LLGestureManager::onLoadComplete(LLVFS *vfs, + const LLUUID& asset_uuid, + LLAssetType::EType type, + void* user_data, S32 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; + + gGestureManager.mLoadingCount--; + + if (0 == status) + { + LLVFile file(vfs, asset_uuid, type, LLVFile::READ); + S32 size = file.getSize(); + + char* buffer = new char[size+1]; + file.read((U8*)buffer, size); + // ensure there's a trailing NULL so strlen will work. + buffer[size] = '\0'; + + LLMultiGesture* gesture = new LLMultiGesture(); + + LLDataPackerAsciiBuffer dp(buffer, size+1); + BOOL ok = gesture->deserialize(dp); + + if (ok) + { + if (deactivate_similar) + { + gGestureManager.deactivateSimilarGestures(gesture, item_id); + + // Display deactivation message if this was the last of the bunch. + if (gGestureManager.mLoadingCount == 0 + && gGestureManager.mDeactivateSimilarNames.length() > 0) + { + // we're done with this set of deactivations + LLString::format_map_t args; + args["[NAMES]"] = gGestureManager.mDeactivateSimilarNames; + LLNotifyBox::showXml("DeactivatedGesturesTrigger", args); + } + } + + // Everything has been successful. Add to the active list. + gGestureManager.mActive[item_id] = gesture; + 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(); + } + + gGestureManager.notifyObservers(); + } + else + { + llwarns << "Unable to load gesture" << llendl; + + gGestureManager.mActive.erase(item_id); + + delete gesture; + gesture = NULL; + } + + delete [] buffer; + buffer = NULL; + } + else + { + if( gViewerStats ) + { + gViewerStats->incStat( LLViewerStats::ST_DOWNLOAD_FAILED ); + } + + if( LL_ERR_ASSET_REQUEST_NOT_IN_DATABASE == status || + LL_ERR_FILE_EMPTY == status) + { + LLNotifyBox::showXml("GestureMissing"); + } + else + { + LLNotifyBox::showXml("UnableToLoadGesture"); + } + + llwarns << "Problem loading gesture: " << status << llendl; + + gGestureManager.mActive.erase(item_id); + } +} + + +void LLGestureManager::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 LLGestureManager::stopGesture(const LLUUID& item_id) +{ + item_map_t::iterator it = mActive.find(item_id); + if (it == mActive.end()) return; + + LLMultiGesture* gesture = (*it).second; + if (!gesture) return; + + stopGesture(gesture); +} + + +void LLGestureManager::addObserver(LLGestureManagerObserver* observer) +{ + mObservers.push_back(observer); +} + +void LLGestureManager::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 LLGestureManager::notifyObservers() +{ + lldebugs << "LLGestureManager::notifyObservers" << llendl; + + std::vector<LLGestureManagerObserver*> observers = mObservers; + + std::vector<LLGestureManagerObserver*>::iterator it; + for (it = observers.begin(); it != observers.end(); ++it) + { + LLGestureManagerObserver* observer = *it; + observer->changed(); + } +} + +BOOL LLGestureManager::matchPrefix(const std::string& in_str, std::string* out_str) +{ + S32 in_len = in_str.length(); + + 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 (in_len > (S32)trigger.length()) + { + // too short, bail out + continue; + } + + LLString trigger_trunc = trigger; + LLString::truncate(trigger_trunc, in_len); + if (!LLString::compareInsensitive(in_str.c_str(), trigger_trunc.c_str())) + { + *out_str = trigger; + return TRUE; + } + } + } + return FALSE; +} + + +void LLGestureManager::getItemIDs(std::vector<LLUUID>* ids) +{ + item_map_t::const_iterator it; + for (it = mActive.begin(); it != mActive.end(); ++it) + { + ids->push_back(it->first); + } +} |