PageRenderTime 249ms CodeModel.GetById 54ms app.highlight 147ms RepoModel.GetById 41ms app.codeStats 0ms

/indra/newview/llfloatergesture.cpp

https://bitbucket.org/lindenlab/viewer-beta/
C++ | 633 lines | 510 code | 71 blank | 52 comment | 88 complexity | 89e8585e135e1deb18d820089847d686 MD5 | raw file
  1/** 
  2 * @file llfloatergesture.cpp
  3 * @brief Read-only list of gestures from your inventory.
  4 *
  5 * $LicenseInfo:firstyear=2002&license=viewerlgpl$
  6 * Second Life Viewer Source Code
  7 * Copyright (C) 2010, Linden Research, Inc.
  8 * 
  9 * This library is free software; you can redistribute it and/or
 10 * modify it under the terms of the GNU Lesser General Public
 11 * License as published by the Free Software Foundation;
 12 * version 2.1 of the License only.
 13 * 
 14 * This library is distributed in the hope that it will be useful,
 15 * but WITHOUT ANY WARRANTY; without even the implied warranty of
 16 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
 17 * Lesser General Public License for more details.
 18 * 
 19 * You should have received a copy of the GNU Lesser General Public
 20 * License along with this library; if not, write to the Free Software
 21 * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA  02110-1301  USA
 22 * 
 23 * Linden Research, Inc., 945 Battery Street, San Francisco, CA  94111  USA
 24 * $/LicenseInfo$
 25 */
 26
 27#include "llviewerprecompiledheaders.h"
 28
 29#include "llfloatergesture.h"
 30
 31#include "llinventory.h"
 32#include "llinventorybridge.h"
 33#include "llinventoryfunctions.h"
 34#include "llinventorymodel.h"
 35#include "llinventoryclipboard.h"
 36
 37#include "llagent.h"
 38#include "llappearancemgr.h"
 39#include "llclipboard.h"
 40#include "llgesturemgr.h"
 41#include "llkeyboard.h"
 42#include "llmenugl.h"
 43#include "llmultigesture.h"
 44#include "llpreviewgesture.h"
 45#include "llscrolllistctrl.h"
 46#include "lltrans.h"
 47#include "llviewergesture.h"
 48#include "llviewermenu.h" 
 49#include "llviewerinventory.h"
 50#include "llviewercontrol.h"
 51
 52BOOL item_name_precedes( LLInventoryItem* a, LLInventoryItem* b )
 53{
 54	return LLStringUtil::precedesDict( a->getName(), b->getName() );
 55}
 56
 57class LLFloaterGestureObserver : public LLGestureManagerObserver
 58{
 59public:
 60	LLFloaterGestureObserver(LLFloaterGesture* floater) : mFloater(floater) {}
 61	virtual ~LLFloaterGestureObserver() {}
 62	virtual void changed() { mFloater->refreshAll(); }
 63
 64private:
 65	LLFloaterGesture* mFloater;
 66};
 67//-----------------------------
 68// GestureCallback
 69//-----------------------------
 70
 71class GestureShowCallback : public LLInventoryCallback
 72{
 73public:
 74	void fire(const LLUUID &inv_item)
 75	{
 76		LLPreviewGesture::show(inv_item, LLUUID::null);
 77	}
 78};
 79
 80class GestureCopiedCallback : public LLInventoryCallback
 81{
 82private:
 83	LLFloaterGesture* mFloater;
 84	
 85public:
 86	GestureCopiedCallback(LLFloaterGesture* floater): mFloater(floater)
 87	{}
 88	void fire(const LLUUID &inv_item)
 89	{
 90		if(mFloater)
 91		{
 92			mFloater->addGesture(inv_item,NULL,mFloater->getChild<LLScrollListCtrl>("gesture_list"));
 93		}
 94	}
 95};
 96
 97//---------------------------------------------------------------------------
 98// LLFloaterGesture
 99//---------------------------------------------------------------------------
100LLFloaterGesture::LLFloaterGesture(const LLSD& key)
101	: LLFloater(key)
102{
103	mObserver = new LLFloaterGestureObserver(this);
104	LLGestureMgr::instance().addObserver(mObserver);
105
106	mCommitCallbackRegistrar.add("Gesture.Action.ToogleActiveState", boost::bind(&LLFloaterGesture::onActivateBtnClick, this));
107	mCommitCallbackRegistrar.add("Gesture.Action.ShowPreview", boost::bind(&LLFloaterGesture::onClickEdit, this));
108	mCommitCallbackRegistrar.add("Gesture.Action.CopyPaste", boost::bind(&LLFloaterGesture::onCopyPasteAction, this, _2));
109	mCommitCallbackRegistrar.add("Gesture.Action.SaveToCOF", boost::bind(&LLFloaterGesture::addToCurrentOutFit, this));
110
111	mEnableCallbackRegistrar.add("Gesture.EnableAction", boost::bind(&LLFloaterGesture::isActionEnabled, this, _2));
112}
113
114void LLFloaterGesture::done()
115{
116	//this method can be called twice: for GestureFolder and once after loading all sudir of GestureFolder
117	if (gInventory.isCategoryComplete(mGestureFolderID))
118	{
119		LL_DEBUGS("Gesture")<< "mGestureFolderID loaded" << LL_ENDL;
120		// we load only gesture folder without childred.
121		LLInventoryModel::cat_array_t* categories;
122		LLInventoryModel::item_array_t* items;
123		uuid_vec_t unloaded_folders;
124		LL_DEBUGS("Gesture")<< "Get subdirs of Gesture Folder...." << LL_ENDL;
125		gInventory.getDirectDescendentsOf(mGestureFolderID, categories, items);
126		if (categories->empty())
127		{
128			gInventory.removeObserver(this);
129			LL_INFOS("Gesture")<< "Gesture dos NOT contains sub-directories."<< LL_ENDL;
130			return;
131		}
132		LL_DEBUGS("Gesture")<< "There are " << categories->size() << " Folders "<< LL_ENDL;
133		for (LLInventoryModel::cat_array_t::iterator it = categories->begin(); it != categories->end(); it++)
134		{
135			if (!gInventory.isCategoryComplete(it->get()->getUUID()))
136			{
137				unloaded_folders.push_back(it->get()->getUUID());
138				LL_DEBUGS("Gesture")<< it->get()->getName()<< " Folder added to fetchlist"<< LL_ENDL;
139			}
140
141		}
142		if (!unloaded_folders.empty())
143		{
144			LL_DEBUGS("Gesture")<< "Fetching subdirectories....." << LL_ENDL;
145			setFetchIDs(unloaded_folders);
146			startFetch();
147		}
148		else
149		{
150			LL_DEBUGS("Gesture")<< "All Gesture subdirectories have been loaded."<< LL_ENDL;
151			gInventory.removeObserver(this);
152			buildGestureList();
153		}
154	}
155	else
156	{
157		LL_WARNS("Gesture")<< "Gesture list was NOT loaded"<< LL_ENDL;
158	}
159}
160
161// virtual
162LLFloaterGesture::~LLFloaterGesture()
163{
164	LLGestureMgr::instance().removeObserver(mObserver);
165	delete mObserver;
166	mObserver = NULL;
167	gInventory.removeObserver(this);
168}
169
170// virtual
171BOOL LLFloaterGesture::postBuild()
172{
173	std::string label;
174
175	label = getTitle();
176	
177	setTitle(label);
178	mGestureList = getChild<LLScrollListCtrl>("gesture_list");
179	mGestureList->setCommitCallback(boost::bind(&LLFloaterGesture::onCommitList, this));
180	mGestureList->setDoubleClickCallback(boost::bind(&LLFloaterGesture::onClickPlay, this));
181
182	getChild<LLUICtrl>("edit_btn")->setCommitCallback(boost::bind(&LLFloaterGesture::onClickEdit, this));
183
184	getChild<LLUICtrl>("play_btn")->setCommitCallback(boost::bind(&LLFloaterGesture::onClickPlay, this));
185	getChild<LLUICtrl>("stop_btn")->setCommitCallback(boost::bind(&LLFloaterGesture::onClickPlay, this));
186	getChild<LLButton>("activate_btn")->setClickedCallback(boost::bind(&LLFloaterGesture::onActivateBtnClick, this));
187	
188	getChild<LLUICtrl>("new_gesture_btn")->setCommitCallback(boost::bind(&LLFloaterGesture::onClickNew, this));
189	getChild<LLButton>("del_btn")->setClickedCallback(boost::bind(&LLFloaterGesture::onDeleteSelected, this));
190
191	getChildView("play_btn")->setVisible( true);
192	getChildView("stop_btn")->setVisible( false);
193	setDefaultBtn("play_btn");
194	mGestureFolderID = gInventory.findCategoryUUIDForType(LLFolderType::FT_GESTURE, false);
195
196	uuid_vec_t folders;
197	folders.push_back(mGestureFolderID);
198	//perform loading Gesture directory anyway to make sure that all subdirectory are loaded too. See method done() for details.
199	gInventory.addObserver(this);
200	setFetchIDs(folders);
201	startFetch();
202
203	if (mGestureList)
204	{
205		buildGestureList();
206	
207		mGestureList->setFocus(TRUE);
208
209		const BOOL ascending = TRUE;
210		mGestureList->sortByColumn(std::string("name"), ascending);
211		mGestureList->selectFirstItem();
212	}
213	
214	// Update button labels
215	onCommitList();
216	
217	return TRUE;
218}
219
220
221void LLFloaterGesture::refreshAll()
222{
223	if (!mGestureList) return;
224
225	buildGestureList();
226
227	if (mSelectedID.isNull())
228	{
229		mGestureList->selectFirstItem();
230	}
231	else
232	{
233		if (! mGestureList->setCurrentByID(mSelectedID))
234		{
235			mGestureList->selectFirstItem();
236		}
237	}
238
239	// Update button labels
240	onCommitList();
241}
242
243void LLFloaterGesture::buildGestureList()
244{
245	S32 scroll_pos = mGestureList->getScrollPos();
246	uuid_vec_t selected_items;
247	getSelectedIds(selected_items);
248	LL_DEBUGS("Gesture")<< "Rebuilding gesture list "<< LL_ENDL;
249	mGestureList->deleteAllItems();
250
251	LLGestureMgr::item_map_t::const_iterator it;
252	const LLGestureMgr::item_map_t& active_gestures = LLGestureMgr::instance().getActiveGestures();
253	for (it = active_gestures.begin(); it != active_gestures.end(); ++it)
254	{
255		addGesture(it->first,it->second, mGestureList);
256	}
257	if (gInventory.isCategoryComplete(mGestureFolderID))
258	{
259		LLIsType is_gesture(LLAssetType::AT_GESTURE);
260		LLInventoryModel::cat_array_t categories;
261		LLInventoryModel::item_array_t items;
262		gInventory.collectDescendentsIf(mGestureFolderID, categories, items,
263				LLInventoryModel::EXCLUDE_TRASH, is_gesture);
264
265		for (LLInventoryModel::item_array_t::iterator it = items.begin(); it!= items.end(); ++it)
266		{
267			LLInventoryItem* item = it->get();
268			if (active_gestures.find(item->getUUID()) == active_gestures.end())
269			{
270				// if gesture wasn't loaded yet, we can display only name
271				addGesture(item->getUUID(), NULL, mGestureList);
272			}
273		}
274	}
275
276	// attempt to preserve scroll position through re-builds
277	// since we do re-build whenever something gets dirty
278	for(uuid_vec_t::iterator it = selected_items.begin(); it != selected_items.end(); it++)
279	{
280		mGestureList->selectByID(*it);
281	}
282	mGestureList->setScrollPos(scroll_pos);
283}
284
285void LLFloaterGesture::addGesture(const LLUUID& item_id , LLMultiGesture* gesture,LLCtrlListInterface * list )
286{
287	// Note: Can have NULL item if inventory hasn't arrived yet.
288	static std::string item_name = getString("loading");
289	LLInventoryItem* item = gInventory.getItem(item_id);
290	if (item)
291	{
292		item_name = item->getName();
293	}
294
295	static std::string font_style = "NORMAL";
296	// If gesture is playing, bold it
297
298	LLSD element;
299	element["id"] = item_id;
300
301	if (gesture)
302	{
303		if (gesture->mPlaying)
304		{
305			font_style = "BOLD";
306		}
307		item_name = gesture->mName;
308		element["columns"][0]["column"] = "trigger";
309		element["columns"][0]["value"] = gesture->mTrigger;
310		element["columns"][0]["font"]["name"] = "SANSSERIF";
311		element["columns"][0]["font"]["style"] = font_style;
312
313		std::string key_string = LLKeyboard::stringFromKey(gesture->mKey);
314		std::string buffer;
315
316		if (gesture->mKey == KEY_NONE)
317		{
318			buffer = "---";
319			key_string = "~~~"; // alphabetize to end
320		}
321		else
322		{
323			buffer = LLKeyboard::stringFromAccelerator(gesture->mMask,
324					gesture->mKey);
325		}
326
327		element["columns"][1]["column"] = "shortcut";
328		element["columns"][1]["value"] = buffer;
329		element["columns"][1]["font"]["name"] = "SANSSERIF";
330		element["columns"][1]["font"]["style"] = font_style;
331
332		// hidden column for sorting
333		element["columns"][2]["column"] = "key";
334		element["columns"][2]["value"] = key_string;
335		element["columns"][2]["font"]["name"] = "SANSSERIF";
336		element["columns"][2]["font"]["style"] = font_style;
337
338		// Only add "playing" if we've got the name, less confusing. JC
339		if (item && gesture->mPlaying)
340		{
341			item_name += " " + getString("playing");
342		}
343		element["columns"][3]["column"] = "name";
344		element["columns"][3]["value"] = item_name;
345		element["columns"][3]["font"]["name"] = "SANSSERIF";
346		element["columns"][3]["font"]["style"] = font_style;
347	}
348	else
349	{
350		element["columns"][0]["column"] = "trigger";
351		element["columns"][0]["value"] = "";
352		element["columns"][0]["font"]["name"] = "SANSSERIF";
353		element["columns"][0]["font"]["style"] = font_style;
354		element["columns"][1]["column"] = "shortcut";
355		element["columns"][1]["value"] = "---";
356		element["columns"][1]["font"]["name"] = "SANSSERIF";
357		element["columns"][1]["font"]["style"] = font_style;
358		element["columns"][2]["column"] = "key";
359		element["columns"][2]["value"] = "~~~";
360		element["columns"][2]["font"]["name"] = "SANSSERIF";
361		element["columns"][2]["font"]["style"] = font_style;
362		element["columns"][3]["column"] = "name";
363		element["columns"][3]["value"] = item_name;
364		element["columns"][3]["font"]["name"] = "SANSSERIF";
365		element["columns"][3]["font"]["style"] = font_style;
366	}
367
368	LL_DEBUGS("Gesture") << "Added gesture [" << item_name << "]" << LL_ENDL;
369
370	LLScrollListItem* sl_item = list->addElement(element, ADD_BOTTOM);
371	if(sl_item)
372	{
373		LLFontGL::StyleFlags style = LLGestureMgr::getInstance()->isGestureActive(item_id) ? LLFontGL::BOLD : LLFontGL::NORMAL;
374		// *TODO find out why ["font"]["style"] does not affect font style
375		((LLScrollListText*)sl_item->getColumn(0))->setFontStyle(style);
376	}
377}
378
379void LLFloaterGesture::getSelectedIds(uuid_vec_t& ids)
380{
381	std::vector<LLScrollListItem*> items = mGestureList->getAllSelected();
382	for(std::vector<LLScrollListItem*>::const_iterator it = items.begin(); it != items.end(); it++)
383	{
384		ids.push_back((*it)->getUUID());
385	}
386}
387
388bool LLFloaterGesture::isActionEnabled(const LLSD& command)
389{
390	// paste copy_uuid edit_gesture
391	std::string command_name = command.asString();
392	if("paste" == command_name)
393	{
394		if(!LLInventoryClipboard::instance().hasContents())
395			return false;
396
397		LLDynamicArray<LLUUID> ids;
398		LLInventoryClipboard::instance().retrieve(ids);
399		for(LLDynamicArray<LLUUID>::iterator it = ids.begin(); it != ids.end(); it++)
400		{
401			LLInventoryItem* item = gInventory.getItem(*it);
402			
403			if(item && item->getInventoryType() == LLInventoryType::IT_GESTURE)
404			{
405				return true;
406			}
407		}
408		return false;
409	}
410	else if("copy_uuid" == command_name || "edit_gesture" == command_name)
411	{
412		return	mGestureList->getAllSelected().size() == 1;
413	}
414	return true;
415}
416
417void LLFloaterGesture::onClickPlay()
418{
419	const LLUUID& item_id = mGestureList->getCurrentID();
420	if(item_id.isNull()) return;
421
422	LL_DEBUGS("Gesture")<<" Trying to play gesture id: "<< item_id <<LL_ENDL;
423	if(!LLGestureMgr::instance().isGestureActive(item_id))
424	{
425		// we need to inform server about gesture activating to be consistent with LLPreviewGesture and  LLGestureComboList.
426		BOOL inform_server = TRUE;
427		BOOL deactivate_similar = FALSE;
428		LLGestureMgr::instance().setGestureLoadedCallback(item_id, boost::bind(&LLFloaterGesture::playGesture, this, item_id));
429		LLViewerInventoryItem *item = gInventory.getItem(item_id);
430		llassert(item);
431		if (item)
432		{
433			LLGestureMgr::instance().activateGestureWithAsset(item_id, item->getAssetUUID(), inform_server, deactivate_similar);
434			LL_DEBUGS("Gesture")<< "Activating gesture with inventory ID: " << item_id <<LL_ENDL;
435		}
436	}
437	else
438	{
439		playGesture(item_id);
440	}
441}
442
443void LLFloaterGesture::onClickNew()
444{
445	LLPointer<LLInventoryCallback> cb = new GestureShowCallback();
446	create_inventory_item(gAgent.getID(), gAgent.getSessionID(),
447		LLUUID::null, LLTransactionID::tnull, "New Gesture", "", LLAssetType::AT_GESTURE,
448		LLInventoryType::IT_GESTURE, NOT_WEARABLE, PERM_MOVE | PERM_TRANSFER, cb);
449}
450
451void LLFloaterGesture::onActivateBtnClick()
452{
453	uuid_vec_t ids;
454	getSelectedIds(ids);
455	if(ids.empty())
456		return;
457
458	LLGestureMgr* gm = LLGestureMgr::getInstance();
459	uuid_vec_t::const_iterator it = ids.begin();
460	BOOL first_gesture_state = gm->isGestureActive(*it);
461	BOOL is_mixed = FALSE;
462	while( ++it != ids.end() )
463	{
464		if(first_gesture_state != gm->isGestureActive(*it))
465		{
466			is_mixed = TRUE;
467			break;
468		}
469	}
470	for(uuid_vec_t::const_iterator it = ids.begin(); it != ids.end(); it++)
471	{
472		if(is_mixed)
473		{
474			gm->activateGesture(*it);
475		}
476		else
477		{
478			if(first_gesture_state)
479			{
480				gm->deactivateGesture(*it);
481			}
482			else
483			{
484				gm->activateGesture(*it);
485			}
486		}
487	}
488}
489
490void LLFloaterGesture::onCopyPasteAction(const LLSD& command)
491{
492	std::string command_name  = command.asString();
493	// since we select this comman inventory item had  already arrived .
494	if("copy_gesture" == command_name)
495	{
496		uuid_vec_t ids;
497		getSelectedIds(ids);
498		// make sure that clopboard is empty
499		LLInventoryClipboard::instance().reset();
500		for(uuid_vec_t::iterator it = ids.begin(); it != ids.end(); it++)
501		{
502			LLInventoryItem* item = gInventory.getItem(*it);
503			if(item  && item->getInventoryType() == LLInventoryType::IT_GESTURE)
504			{
505				LLInventoryClipboard::instance().add(item->getUUID());
506			}
507		}
508	}
509	else if ("paste" == command_name)
510	{
511		LLInventoryClipboard& clipbord = LLInventoryClipboard::instance();
512		LLDynamicArray<LLUUID> ids;
513		clipbord.retrieve(ids);
514		if(ids.empty() || !gInventory.isCategoryComplete(mGestureFolderID))
515			return;
516		LLInventoryCategory* gesture_dir = gInventory.getCategory(mGestureFolderID);
517		llassert(gesture_dir);
518		LLPointer<GestureCopiedCallback> cb = new GestureCopiedCallback(this);
519
520		for(LLDynamicArray<LLUUID>::iterator it = ids.begin(); it != ids.end(); it++)
521		{
522			LLInventoryItem* item = gInventory.getItem(*it);
523			if(gesture_dir && item && item->getInventoryType() == LLInventoryType::IT_GESTURE)
524			{
525				LLStringUtil::format_map_t string_args;
526				string_args["[COPY_NAME]"] = item->getName();
527				LL_DEBUGS("Gesture")<< "Copying gesture " << item->getName() << "  "<< item->getUUID() << " into "
528										<< gesture_dir->getName() << "  "<< gesture_dir->getUUID() << LL_ENDL;
529				copy_inventory_item(gAgent.getID(), item->getPermissions().getOwner(), item->getUUID(), 
530						gesture_dir->getUUID(), getString("copy_name", string_args), cb);
531			}
532		}
533		clipbord.reset();
534	}
535	else if ("copy_uuid" == command_name)
536	{
537		gClipboard.copyFromString(utf8str_to_wstring(mGestureList->getCurrentID().asString()), mGestureList->getCurrentID());
538	}
539}
540
541void LLFloaterGesture::onClickEdit()
542{
543	const LLUUID& item_id = mGestureList->getCurrentID();
544
545	LLInventoryItem* item = gInventory.getItem(item_id);
546	if (!item) return;
547
548	LLPreviewGesture* previewp = LLPreviewGesture::show(item_id, LLUUID::null);
549	if (!previewp->getHost())
550	{
551		previewp->setRect(gFloaterView->findNeighboringPosition(this, previewp));
552	}
553}
554
555void LLFloaterGesture::onCommitList()
556{
557	const LLUUID& item_id = mGestureList->getCurrentID();
558
559	mSelectedID = item_id;
560	if (LLGestureMgr::instance().isGesturePlaying(item_id))
561	{
562		getChildView("play_btn")->setVisible( false);
563		getChildView("stop_btn")->setVisible( true);
564	}
565	else
566	{
567		getChildView("play_btn")->setVisible( true);
568		getChildView("stop_btn")->setVisible( false);
569	}
570}
571
572void LLFloaterGesture::onDeleteSelected()
573{
574	uuid_vec_t ids;
575	getSelectedIds(ids);
576	if(ids.empty())
577		return;
578
579	const LLUUID trash_id = gInventory.findCategoryUUIDForType(LLFolderType::FT_TRASH);
580	LLGestureMgr* gm = LLGestureMgr::getInstance();
581	for(uuid_vec_t::const_iterator it = ids.begin(); it != ids.end(); it++)
582	{
583		const LLUUID& selected_item = *it;
584		LLInventoryItem* inv_item = gInventory.getItem(selected_item);
585		if (inv_item && inv_item->getInventoryType() == LLInventoryType::IT_GESTURE)
586		{
587			if(gm->isGestureActive(selected_item))
588			{
589				gm->deactivateGesture(selected_item);
590			}
591			LLInventoryModel::update_list_t update;
592			LLInventoryModel::LLCategoryUpdate old_folder(inv_item->getParentUUID(), -1);
593			update.push_back(old_folder);
594			LLInventoryModel::LLCategoryUpdate new_folder(trash_id, 1);
595			update.push_back(new_folder);
596			gInventory.accountForUpdate(update);
597
598			LLPointer<LLViewerInventoryItem> new_item = new LLViewerInventoryItem(inv_item);
599			new_item->setParent(trash_id);
600			// no need to restamp it though it's a move into trash because
601			// it's a brand new item already.
602			new_item->updateParentOnServer(FALSE);
603			gInventory.updateItem(new_item);
604		}
605	}
606	gInventory.notifyObservers();
607	buildGestureList();
608}
609
610void LLFloaterGesture::addToCurrentOutFit()
611{
612	uuid_vec_t ids;
613	getSelectedIds(ids);
614	LLAppearanceMgr* am = LLAppearanceMgr::getInstance();
615	for(uuid_vec_t::const_iterator it = ids.begin(); it != ids.end(); it++)
616	{
617		am->addCOFItemLink(*it);
618	}
619}
620
621void LLFloaterGesture::playGesture(LLUUID item_id)
622{
623	LL_DEBUGS("Gesture")<<"Playing gesture "<< item_id<<LL_ENDL;
624
625	if (LLGestureMgr::instance().isGesturePlaying(item_id))
626	{
627		LLGestureMgr::instance().stopGesture(item_id);
628	}
629	else
630	{
631		LLGestureMgr::instance().playGesture(item_id);
632	}
633}