PageRenderTime 212ms CodeModel.GetById 13ms app.highlight 177ms RepoModel.GetById 1ms app.codeStats 1ms

/indra/newview/llappearancemgr.cpp

https://bitbucket.org/lindenlab/viewer-beta/
C++ | 2239 lines | 1776 code | 267 blank | 196 comment | 280 complexity | ad524e20cc0a499602b9525c211390b7 MD5 | raw file

Large files files are truncated, but you can click here to view the full file

   1/** 
   2 * @file llappearancemgr.cpp
   3 * @brief Manager for initiating appearance changes on the viewer
   4 *
   5 * $LicenseInfo:firstyear=2004&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 "llaccordionctrltab.h"
  30#include "llagent.h"
  31#include "llagentcamera.h"
  32#include "llagentwearables.h"
  33#include "llappearancemgr.h"
  34#include "llattachmentsmgr.h"
  35#include "llcommandhandler.h"
  36#include "lleventtimer.h"
  37#include "llfloatersidepanelcontainer.h"
  38#include "llgesturemgr.h"
  39#include "llinventorybridge.h"
  40#include "llinventoryfunctions.h"
  41#include "llinventoryobserver.h"
  42#include "llnotificationsutil.h"
  43#include "lloutfitobserver.h"
  44#include "lloutfitslist.h"
  45#include "llselectmgr.h"
  46#include "llsidepanelappearance.h"
  47#include "llviewerobjectlist.h"
  48#include "llvoavatar.h"
  49#include "llvoavatarself.h"
  50#include "llviewerregion.h"
  51#include "llwearablelist.h"
  52
  53// RAII thingy to guarantee that a variable gets reset when the Setter
  54// goes out of scope.  More general utility would be handy - TODO:
  55// check boost.
  56class BoolSetter
  57{
  58public:
  59	BoolSetter(bool& var):
  60		mVar(var)
  61	{
  62		mVar = true;
  63	}
  64	~BoolSetter()
  65	{
  66		mVar = false; 
  67	}
  68private:
  69	bool& mVar;
  70};
  71
  72char ORDER_NUMBER_SEPARATOR('@');
  73
  74class LLOutfitUnLockTimer: public LLEventTimer
  75{
  76public:
  77	LLOutfitUnLockTimer(F32 period) : LLEventTimer(period)
  78	{
  79		// restart timer on BOF changed event
  80		LLOutfitObserver::instance().addBOFChangedCallback(boost::bind(
  81				&LLOutfitUnLockTimer::reset, this));
  82		stop();
  83	}
  84
  85	/*virtual*/
  86	BOOL tick()
  87	{
  88		if(mEventTimer.hasExpired())
  89		{
  90			LLAppearanceMgr::instance().setOutfitLocked(false);
  91		}
  92		return FALSE;
  93	}
  94	void stop() { mEventTimer.stop(); }
  95	void start() { mEventTimer.start(); }
  96	void reset() { mEventTimer.reset(); }
  97	BOOL getStarted() { return mEventTimer.getStarted(); }
  98
  99	LLTimer&  getEventTimer() { return mEventTimer;}
 100};
 101
 102// support for secondlife:///app/appearance SLapps
 103class LLAppearanceHandler : public LLCommandHandler
 104{
 105public:
 106	// requests will be throttled from a non-trusted browser
 107	LLAppearanceHandler() : LLCommandHandler("appearance", UNTRUSTED_THROTTLE) {}
 108
 109	bool handle(const LLSD& params, const LLSD& query_map, LLMediaCtrl* web)
 110	{
 111		// support secondlife:///app/appearance/show, but for now we just
 112		// make all secondlife:///app/appearance SLapps behave this way
 113		if (!LLUI::sSettingGroups["config"]->getBOOL("EnableAppearance"))
 114		{
 115			LLNotificationsUtil::add("NoAppearance", LLSD(), LLSD(), std::string("SwitchToStandardSkinAndQuit"));
 116			return true;
 117		}
 118
 119		LLFloaterSidePanelContainer::showPanel("appearance", LLSD());
 120		return true;
 121	}
 122};
 123
 124LLAppearanceHandler gAppearanceHandler;
 125
 126
 127LLUUID findDescendentCategoryIDByName(const LLUUID& parent_id, const std::string& name)
 128{
 129	LLInventoryModel::cat_array_t cat_array;
 130	LLInventoryModel::item_array_t item_array;
 131	LLNameCategoryCollector has_name(name);
 132	gInventory.collectDescendentsIf(parent_id,
 133									cat_array,
 134									item_array,
 135									LLInventoryModel::EXCLUDE_TRASH,
 136									has_name);
 137	if (0 == cat_array.count())
 138		return LLUUID();
 139	else
 140	{
 141		LLViewerInventoryCategory *cat = cat_array.get(0);
 142		if (cat)
 143			return cat->getUUID();
 144		else
 145		{
 146			llwarns << "null cat" << llendl;
 147			return LLUUID();
 148		}
 149	}
 150}
 151
 152class LLWearInventoryCategoryCallback : public LLInventoryCallback
 153{
 154public:
 155	LLWearInventoryCategoryCallback(const LLUUID& cat_id, bool append)
 156	{
 157		mCatID = cat_id;
 158		mAppend = append;
 159	}
 160	void fire(const LLUUID& item_id)
 161	{
 162		/*
 163		 * Do nothing.  We only care about the destructor
 164		 *
 165		 * The reason for this is that this callback is used in a hack where the
 166		 * same callback is given to dozens of items, and the destructor is called
 167		 * after the last item has fired the event and dereferenced it -- if all
 168		 * the events actually fire!
 169		 */
 170	}
 171
 172protected:
 173	~LLWearInventoryCategoryCallback()
 174	{
 175		llinfos << "done all inventory callbacks" << llendl;
 176		
 177		// Is the destructor called by ordinary dereference, or because the app's shutting down?
 178		// If the inventory callback manager goes away, we're shutting down, no longer want the callback.
 179		if( LLInventoryCallbackManager::is_instantiated() )
 180		{
 181			LLAppearanceMgr::instance().wearInventoryCategoryOnAvatar(gInventory.getCategory(mCatID), mAppend);
 182		}
 183		else
 184		{
 185			llwarns << "Dropping unhandled LLWearInventoryCategoryCallback" << llendl;
 186		}
 187	}
 188
 189private:
 190	LLUUID mCatID;
 191	bool mAppend;
 192};
 193
 194
 195//Inventory callback updating "dirty" state when destroyed
 196class LLUpdateDirtyState: public LLInventoryCallback
 197{
 198public:
 199	LLUpdateDirtyState() {}
 200	virtual ~LLUpdateDirtyState()
 201	{
 202		if (LLAppearanceMgr::instanceExists())
 203		{
 204			LLAppearanceMgr::getInstance()->updateIsDirty();
 205		}
 206	}
 207	virtual void fire(const LLUUID&) {}
 208};
 209
 210
 211LLUpdateAppearanceOnDestroy::LLUpdateAppearanceOnDestroy(bool update_base_outfit_ordering):
 212	mFireCount(0),
 213	mUpdateBaseOrder(update_base_outfit_ordering)
 214{
 215}
 216
 217LLUpdateAppearanceOnDestroy::~LLUpdateAppearanceOnDestroy()
 218{
 219	llinfos << "done update appearance on destroy" << llendl;
 220	
 221	if (!LLApp::isExiting())
 222	{
 223		LLAppearanceMgr::instance().updateAppearanceFromCOF(mUpdateBaseOrder);
 224	}
 225}
 226
 227void LLUpdateAppearanceOnDestroy::fire(const LLUUID& inv_item)
 228{
 229	LLViewerInventoryItem* item = (LLViewerInventoryItem*)gInventory.getItem(inv_item);
 230	const std::string item_name = item ? item->getName() : "ITEM NOT FOUND";
 231#ifndef LL_RELEASE_FOR_DOWNLOAD
 232	llinfos << "callback fired [ name:" << item_name << " UUID:" << inv_item << " count:" << mFireCount << " ] " << llendl;
 233#endif
 234	mFireCount++;
 235}
 236
 237struct LLFoundData
 238{
 239	LLFoundData() :
 240		mAssetType(LLAssetType::AT_NONE),
 241		mWearableType(LLWearableType::WT_INVALID),
 242		mWearable(NULL) {}
 243
 244	LLFoundData(const LLUUID& item_id,
 245				const LLUUID& asset_id,
 246				const std::string& name,
 247				const LLAssetType::EType& asset_type,
 248				const LLWearableType::EType& wearable_type,
 249				const bool is_replacement = false
 250		) :
 251		mItemID(item_id),
 252		mAssetID(asset_id),
 253		mName(name),
 254		mAssetType(asset_type),
 255		mWearableType(wearable_type),
 256		mIsReplacement(is_replacement),
 257		mWearable( NULL ) {}
 258	
 259	LLUUID mItemID;
 260	LLUUID mAssetID;
 261	std::string mName;
 262	LLAssetType::EType mAssetType;
 263	LLWearableType::EType mWearableType;
 264	LLWearable* mWearable;
 265	bool mIsReplacement;
 266};
 267
 268	
 269class LLWearableHoldingPattern
 270{
 271public:
 272	LLWearableHoldingPattern();
 273	~LLWearableHoldingPattern();
 274
 275	bool pollFetchCompletion();
 276	void onFetchCompletion();
 277	bool isFetchCompleted();
 278	bool isTimedOut();
 279
 280	void checkMissingWearables();
 281	bool pollMissingWearables();
 282	bool isMissingCompleted();
 283	void recoverMissingWearable(LLWearableType::EType type);
 284	void clearCOFLinksForMissingWearables();
 285	
 286	void onWearableAssetFetch(LLWearable *wearable);
 287	void onAllComplete();
 288
 289	typedef std::list<LLFoundData> found_list_t;
 290	found_list_t& getFoundList();
 291	void eraseTypeToLink(LLWearableType::EType type);
 292	void eraseTypeToRecover(LLWearableType::EType type);
 293	void setObjItems(const LLInventoryModel::item_array_t& items);
 294	void setGestItems(const LLInventoryModel::item_array_t& items);
 295	bool isMostRecent();
 296	void handleLateArrivals();
 297	void resetTime(F32 timeout);
 298	
 299private:
 300	found_list_t mFoundList;
 301	LLInventoryModel::item_array_t mObjItems;
 302	LLInventoryModel::item_array_t mGestItems;
 303	typedef std::set<S32> type_set_t;
 304	type_set_t mTypesToRecover;
 305	type_set_t mTypesToLink;
 306	S32 mResolved;
 307	LLTimer mWaitTime;
 308	bool mFired;
 309	typedef std::set<LLWearableHoldingPattern*> type_set_hp;
 310	static type_set_hp sActiveHoldingPatterns;
 311	bool mIsMostRecent;
 312	std::set<LLWearable*> mLateArrivals;
 313	bool mIsAllComplete;
 314};
 315
 316LLWearableHoldingPattern::type_set_hp LLWearableHoldingPattern::sActiveHoldingPatterns;
 317
 318LLWearableHoldingPattern::LLWearableHoldingPattern():
 319	mResolved(0),
 320	mFired(false),
 321	mIsMostRecent(true),
 322	mIsAllComplete(false)
 323{
 324	if (sActiveHoldingPatterns.size()>0)
 325	{
 326		llinfos << "Creating LLWearableHoldingPattern when "
 327				<< sActiveHoldingPatterns.size()
 328				<< " other attempts are active."
 329				<< " Flagging others as invalid."
 330				<< llendl;
 331		for (type_set_hp::iterator it = sActiveHoldingPatterns.begin();
 332			 it != sActiveHoldingPatterns.end();
 333			 ++it)
 334		{
 335			(*it)->mIsMostRecent = false;
 336		}
 337			 
 338	}
 339	sActiveHoldingPatterns.insert(this);
 340}
 341
 342LLWearableHoldingPattern::~LLWearableHoldingPattern()
 343{
 344	sActiveHoldingPatterns.erase(this);
 345}
 346
 347bool LLWearableHoldingPattern::isMostRecent()
 348{
 349	return mIsMostRecent;
 350}
 351
 352LLWearableHoldingPattern::found_list_t& LLWearableHoldingPattern::getFoundList()
 353{
 354	return mFoundList;
 355}
 356
 357void LLWearableHoldingPattern::eraseTypeToLink(LLWearableType::EType type)
 358{
 359	mTypesToLink.erase(type);
 360}
 361
 362void LLWearableHoldingPattern::eraseTypeToRecover(LLWearableType::EType type)
 363{
 364	mTypesToRecover.erase(type);
 365}
 366
 367void LLWearableHoldingPattern::setObjItems(const LLInventoryModel::item_array_t& items)
 368{
 369	mObjItems = items;
 370}
 371
 372void LLWearableHoldingPattern::setGestItems(const LLInventoryModel::item_array_t& items)
 373{
 374	mGestItems = items;
 375}
 376
 377bool LLWearableHoldingPattern::isFetchCompleted()
 378{
 379	return (mResolved >= (S32)getFoundList().size()); // have everything we were waiting for?
 380}
 381
 382bool LLWearableHoldingPattern::isTimedOut()
 383{
 384	return mWaitTime.hasExpired();
 385}
 386
 387void LLWearableHoldingPattern::checkMissingWearables()
 388{
 389	if (!isMostRecent())
 390	{
 391		llwarns << "skipping because LLWearableHolding pattern is invalid (superceded by later outfit request)" << llendl;
 392	}
 393		
 394	std::vector<S32> found_by_type(LLWearableType::WT_COUNT,0);
 395	std::vector<S32> requested_by_type(LLWearableType::WT_COUNT,0);
 396	for (found_list_t::iterator it = getFoundList().begin(); it != getFoundList().end(); ++it)
 397	{
 398		LLFoundData &data = *it;
 399		if (data.mWearableType < LLWearableType::WT_COUNT)
 400			requested_by_type[data.mWearableType]++;
 401		if (data.mWearable)
 402			found_by_type[data.mWearableType]++;
 403	}
 404
 405	for (S32 type = 0; type < LLWearableType::WT_COUNT; ++type)
 406	{
 407		if (requested_by_type[type] > found_by_type[type])
 408		{
 409			llwarns << "got fewer wearables than requested, type " << type << ": requested " << requested_by_type[type] << ", found " << found_by_type[type] << llendl;
 410		}
 411		if (found_by_type[type] > 0)
 412			continue;
 413		if (
 414			// If at least one wearable of certain types (pants/shirt/skirt)
 415			// was requested but none was found, create a default asset as a replacement.
 416			// In all other cases, don't do anything.
 417			// For critical types (shape/hair/skin/eyes), this will keep the avatar as a cloud 
 418			// due to logic in LLVOAvatarSelf::getIsCloud().
 419			// For non-critical types (tatoo, socks, etc.) the wearable will just be missing.
 420			(requested_by_type[type] > 0) &&  
 421			((type == LLWearableType::WT_PANTS) || (type == LLWearableType::WT_SHIRT) || (type == LLWearableType::WT_SKIRT)))
 422		{
 423			mTypesToRecover.insert(type);
 424			mTypesToLink.insert(type);
 425			recoverMissingWearable((LLWearableType::EType)type);
 426			llwarns << "need to replace " << type << llendl; 
 427		}
 428	}
 429
 430	resetTime(60.0F);
 431	if (!pollMissingWearables())
 432	{
 433		doOnIdleRepeating(boost::bind(&LLWearableHoldingPattern::pollMissingWearables,this));
 434	}
 435}
 436
 437void LLWearableHoldingPattern::onAllComplete()
 438{
 439	if (!isMostRecent())
 440	{
 441		llwarns << "skipping because LLWearableHolding pattern is invalid (superceded by later outfit request)" << llendl;
 442	}
 443
 444	// Activate all gestures in this folder
 445	if (mGestItems.count() > 0)
 446	{
 447		llinfos << "Activating " << mGestItems.count() << " gestures" << llendl;
 448		
 449		LLGestureMgr::instance().activateGestures(mGestItems);
 450		
 451		// Update the inventory item labels to reflect the fact
 452		// they are active.
 453		LLViewerInventoryCategory* catp =
 454			gInventory.getCategory(LLAppearanceMgr::instance().getCOF());
 455		
 456		if (catp)
 457		{
 458			gInventory.updateCategory(catp);
 459			gInventory.notifyObservers();
 460		}
 461	}
 462
 463	// Update wearables.
 464	llinfos << "Updating agent wearables with " << mResolved << " wearable items " << llendl;
 465	LLAppearanceMgr::instance().updateAgentWearables(this, false);
 466	
 467	// Update attachments to match those requested.
 468	if (isAgentAvatarValid())
 469	{
 470		llinfos << "Updating " << mObjItems.count() << " attachments" << llendl;
 471		LLAgentWearables::userUpdateAttachments(mObjItems);
 472	}
 473
 474	if (isFetchCompleted() && isMissingCompleted())
 475	{
 476		// Only safe to delete if all wearable callbacks and all missing wearables completed.
 477		delete this;
 478	}
 479	else
 480	{
 481		mIsAllComplete = true;
 482		handleLateArrivals();
 483	}
 484}
 485
 486void LLWearableHoldingPattern::onFetchCompletion()
 487{
 488	if (!isMostRecent())
 489	{
 490		llwarns << "skipping because LLWearableHolding pattern is invalid (superceded by later outfit request)" << llendl;
 491	}
 492
 493	checkMissingWearables();
 494}
 495
 496// Runs as an idle callback until all wearables are fetched (or we time out).
 497bool LLWearableHoldingPattern::pollFetchCompletion()
 498{
 499	if (!isMostRecent())
 500	{
 501		llwarns << "skipping because LLWearableHolding pattern is invalid (superceded by later outfit request)" << llendl;
 502	}
 503
 504	bool completed = isFetchCompleted();
 505	bool timed_out = isTimedOut();
 506	bool done = completed || timed_out;
 507
 508	if (done)
 509	{
 510		llinfos << "polling, done status: " << completed << " timed out " << timed_out
 511				<< " elapsed " << mWaitTime.getElapsedTimeF32() << llendl;
 512
 513		mFired = true;
 514		
 515		if (timed_out)
 516		{
 517			llwarns << "Exceeded max wait time for wearables, updating appearance based on what has arrived" << llendl;
 518		}
 519
 520		onFetchCompletion();
 521	}
 522	return done;
 523}
 524
 525class RecoveredItemLinkCB: public LLInventoryCallback
 526{
 527public:
 528	RecoveredItemLinkCB(LLWearableType::EType type, LLWearable *wearable, LLWearableHoldingPattern* holder):
 529		mHolder(holder),
 530		mWearable(wearable),
 531		mType(type)
 532	{
 533	}
 534	void fire(const LLUUID& item_id)
 535	{
 536		if (!mHolder->isMostRecent())
 537		{
 538			llwarns << "skipping because LLWearableHolding pattern is invalid (superceded by later outfit request)" << llendl;
 539		}
 540
 541		llinfos << "Recovered item link for type " << mType << llendl;
 542		mHolder->eraseTypeToLink(mType);
 543		// Add wearable to FoundData for actual wearing
 544		LLViewerInventoryItem *item = gInventory.getItem(item_id);
 545		LLViewerInventoryItem *linked_item = item ? item->getLinkedItem() : NULL;
 546
 547		if (linked_item)
 548		{
 549			gInventory.addChangedMask(LLInventoryObserver::LABEL, linked_item->getUUID());
 550			
 551			if (item)
 552			{
 553				LLFoundData found(linked_item->getUUID(),
 554								  linked_item->getAssetUUID(),
 555								  linked_item->getName(),
 556								  linked_item->getType(),
 557								  linked_item->isWearableType() ? linked_item->getWearableType() : LLWearableType::WT_INVALID,
 558								  true // is replacement
 559					);
 560				found.mWearable = mWearable;
 561				mHolder->getFoundList().push_front(found);
 562			}
 563			else
 564			{
 565				llwarns << "inventory item not found for recovered wearable" << llendl;
 566			}
 567		}
 568		else
 569		{
 570			llwarns << "inventory link not found for recovered wearable" << llendl;
 571		}
 572	}
 573private:
 574	LLWearableHoldingPattern* mHolder;
 575	LLWearable *mWearable;
 576	LLWearableType::EType mType;
 577};
 578
 579class RecoveredItemCB: public LLInventoryCallback
 580{
 581public:
 582	RecoveredItemCB(LLWearableType::EType type, LLWearable *wearable, LLWearableHoldingPattern* holder):
 583		mHolder(holder),
 584		mWearable(wearable),
 585		mType(type)
 586	{
 587	}
 588	void fire(const LLUUID& item_id)
 589	{
 590		if (!mHolder->isMostRecent())
 591		{
 592			llwarns << "skipping because LLWearableHolding pattern is invalid (superceded by later outfit request)" << llendl;
 593		}
 594
 595		llinfos << "Recovered item for type " << mType << llendl;
 596		LLViewerInventoryItem *itemp = gInventory.getItem(item_id);
 597		mWearable->setItemID(item_id);
 598		LLPointer<LLInventoryCallback> cb = new RecoveredItemLinkCB(mType,mWearable,mHolder);
 599		mHolder->eraseTypeToRecover(mType);
 600		llassert(itemp);
 601		if (itemp)
 602		{
 603			link_inventory_item( gAgent.getID(),
 604					     item_id,
 605					     LLAppearanceMgr::instance().getCOF(),
 606					     itemp->getName(),
 607						 itemp->getDescription(),
 608					     LLAssetType::AT_LINK,
 609					     cb);
 610		}
 611	}
 612private:
 613	LLWearableHoldingPattern* mHolder;
 614	LLWearable *mWearable;
 615	LLWearableType::EType mType;
 616};
 617
 618void LLWearableHoldingPattern::recoverMissingWearable(LLWearableType::EType type)
 619{
 620	if (!isMostRecent())
 621	{
 622		llwarns << "skipping because LLWearableHolding pattern is invalid (superceded by later outfit request)" << llendl;
 623	}
 624	
 625		// Try to recover by replacing missing wearable with a new one.
 626	LLNotificationsUtil::add("ReplacedMissingWearable");
 627	lldebugs << "Wearable " << LLWearableType::getTypeLabel(type)
 628			 << " could not be downloaded.  Replaced inventory item with default wearable." << llendl;
 629	LLWearable* wearable = LLWearableList::instance().createNewWearable(type);
 630
 631	// Add a new one in the lost and found folder.
 632	const LLUUID lost_and_found_id = gInventory.findCategoryUUIDForType(LLFolderType::FT_LOST_AND_FOUND);
 633	LLPointer<LLInventoryCallback> cb = new RecoveredItemCB(type,wearable,this);
 634
 635	create_inventory_item(gAgent.getID(),
 636						  gAgent.getSessionID(),
 637						  lost_and_found_id,
 638						  wearable->getTransactionID(),
 639						  wearable->getName(),
 640						  wearable->getDescription(),
 641						  wearable->getAssetType(),
 642						  LLInventoryType::IT_WEARABLE,
 643						  wearable->getType(),
 644						  wearable->getPermissions().getMaskNextOwner(),
 645						  cb);
 646}
 647
 648bool LLWearableHoldingPattern::isMissingCompleted()
 649{
 650	return mTypesToLink.size()==0 && mTypesToRecover.size()==0;
 651}
 652
 653void LLWearableHoldingPattern::clearCOFLinksForMissingWearables()
 654{
 655	for (found_list_t::iterator it = getFoundList().begin(); it != getFoundList().end(); ++it)
 656	{
 657		LLFoundData &data = *it;
 658		if ((data.mWearableType < LLWearableType::WT_COUNT) && (!data.mWearable))
 659		{
 660			// Wearable link that was never resolved; remove links to it from COF
 661			llinfos << "removing link for unresolved item " << data.mItemID.asString() << llendl;
 662			LLAppearanceMgr::instance().removeCOFItemLinks(data.mItemID,false);
 663		}
 664	}
 665}
 666
 667bool LLWearableHoldingPattern::pollMissingWearables()
 668{
 669	if (!isMostRecent())
 670	{
 671		llwarns << "skipping because LLWearableHolding pattern is invalid (superceded by later outfit request)" << llendl;
 672	}
 673	
 674	bool timed_out = isTimedOut();
 675	bool missing_completed = isMissingCompleted();
 676	bool done = timed_out || missing_completed;
 677
 678	if (!done)
 679	{
 680		llinfos << "polling missing wearables, waiting for items " << mTypesToRecover.size()
 681				<< " links " << mTypesToLink.size()
 682				<< " wearables, timed out " << timed_out
 683				<< " elapsed " << mWaitTime.getElapsedTimeF32()
 684				<< " done " << done << llendl;
 685	}
 686
 687	if (done)
 688	{
 689		gAgentAvatarp->debugWearablesLoaded();
 690
 691		// BAP - if we don't call clearCOFLinksForMissingWearables()
 692		// here, we won't have to add the link back in later if the
 693		// wearable arrives late.  This is to avoid corruption of
 694		// wearable ordering info.  Also has the effect of making
 695		// unworn item links visible in the COF under some
 696		// circumstances.
 697
 698		//clearCOFLinksForMissingWearables();
 699		onAllComplete();
 700	}
 701	return done;
 702}
 703
 704// Handle wearables that arrived after the timeout period expired.
 705void LLWearableHoldingPattern::handleLateArrivals()
 706{
 707	// Only safe to run if we have previously finished the missing
 708	// wearables and other processing - otherwise we could be in some
 709	// intermediate state - but have not been superceded by a later
 710	// outfit change request.
 711	if (mLateArrivals.size() == 0)
 712	{
 713		// Nothing to process.
 714		return;
 715	}
 716	if (!isMostRecent())
 717	{
 718		llwarns << "Late arrivals not handled - outfit change no longer valid" << llendl;
 719	}
 720	if (!mIsAllComplete)
 721	{
 722		llwarns << "Late arrivals not handled - in middle of missing wearables processing" << llendl;
 723	}
 724
 725	llinfos << "Need to handle " << mLateArrivals.size() << " late arriving wearables" << llendl;
 726
 727	// Update mFoundList using late-arriving wearables.
 728	std::set<LLWearableType::EType> replaced_types;
 729	for (LLWearableHoldingPattern::found_list_t::iterator iter = getFoundList().begin();
 730		 iter != getFoundList().end(); ++iter)
 731	{
 732		LLFoundData& data = *iter;
 733		for (std::set<LLWearable*>::iterator wear_it = mLateArrivals.begin();
 734			 wear_it != mLateArrivals.end();
 735			 ++wear_it)
 736		{
 737			LLWearable *wearable = *wear_it;
 738
 739			if(wearable->getAssetID() == data.mAssetID)
 740			{
 741				data.mWearable = wearable;
 742
 743				replaced_types.insert(data.mWearableType);
 744
 745				// BAP - if we didn't call
 746				// clearCOFLinksForMissingWearables() earlier, we
 747				// don't need to restore the link here.  Fixes
 748				// wearable ordering problems.
 749
 750				// LLAppearanceMgr::instance().addCOFItemLink(data.mItemID,false);
 751
 752				// BAP failing this means inventory or asset server
 753				// are corrupted in a way we don't handle.
 754				llassert((data.mWearableType < LLWearableType::WT_COUNT) && (wearable->getType() == data.mWearableType));
 755				break;
 756			}
 757		}
 758	}
 759
 760	// Remove COF links for any default wearables previously used to replace the late arrivals.
 761	// All this pussyfooting around with a while loop and explicit
 762	// iterator incrementing is to allow removing items from the list
 763	// without clobbering the iterator we're using to navigate.
 764	LLWearableHoldingPattern::found_list_t::iterator iter = getFoundList().begin();
 765	while (iter != getFoundList().end())
 766	{
 767		LLFoundData& data = *iter;
 768
 769		// If an item of this type has recently shown up, removed the corresponding replacement wearable from COF.
 770		if (data.mWearable && data.mIsReplacement &&
 771			replaced_types.find(data.mWearableType) != replaced_types.end())
 772		{
 773			LLAppearanceMgr::instance().removeCOFItemLinks(data.mItemID,false);
 774			std::list<LLFoundData>::iterator clobber_ator = iter;
 775			++iter;
 776			getFoundList().erase(clobber_ator);
 777		}
 778		else
 779		{
 780			++iter;
 781		}
 782	}
 783
 784	// Clear contents of late arrivals.
 785	mLateArrivals.clear();
 786
 787	// Update appearance based on mFoundList
 788	LLAppearanceMgr::instance().updateAgentWearables(this, false);
 789}
 790
 791void LLWearableHoldingPattern::resetTime(F32 timeout)
 792{
 793	mWaitTime.reset();
 794	mWaitTime.setTimerExpirySec(timeout);
 795}
 796
 797void LLWearableHoldingPattern::onWearableAssetFetch(LLWearable *wearable)
 798{
 799	if (!isMostRecent())
 800	{
 801		llwarns << "skipping because LLWearableHolding pattern is invalid (superceded by later outfit request)" << llendl;
 802	}
 803	
 804	mResolved += 1;  // just counting callbacks, not successes.
 805	llinfos << "resolved " << mResolved << "/" << getFoundList().size() << llendl;
 806	if (!wearable)
 807	{
 808		llwarns << "no wearable found" << llendl;
 809	}
 810
 811	if (mFired)
 812	{
 813		llwarns << "called after holder fired" << llendl;
 814		if (wearable)
 815		{
 816			mLateArrivals.insert(wearable);
 817			if (mIsAllComplete)
 818			{
 819				handleLateArrivals();
 820			}
 821		}
 822		return;
 823	}
 824
 825	if (!wearable)
 826	{
 827		return;
 828	}
 829
 830	for (LLWearableHoldingPattern::found_list_t::iterator iter = getFoundList().begin();
 831		 iter != getFoundList().end(); ++iter)
 832	{
 833		LLFoundData& data = *iter;
 834		if(wearable->getAssetID() == data.mAssetID)
 835		{
 836			// Failing this means inventory or asset server are corrupted in a way we don't handle.
 837			if ((data.mWearableType >= LLWearableType::WT_COUNT) || (wearable->getType() != data.mWearableType))
 838			{
 839				llwarns << "recovered wearable but type invalid. inventory wearable type: " << data.mWearableType << " asset wearable type: " << wearable->getType() << llendl;
 840				break;
 841			}
 842
 843			data.mWearable = wearable;
 844		}
 845	}
 846}
 847
 848static void onWearableAssetFetch(LLWearable* wearable, void* data)
 849{
 850	LLWearableHoldingPattern* holder = (LLWearableHoldingPattern*)data;
 851	holder->onWearableAssetFetch(wearable);
 852}
 853
 854
 855static void removeDuplicateItems(LLInventoryModel::item_array_t& items)
 856{
 857	LLInventoryModel::item_array_t new_items;
 858	std::set<LLUUID> items_seen;
 859	std::deque<LLViewerInventoryItem*> tmp_list;
 860	// Traverse from the front and keep the first of each item
 861	// encountered, so we actually keep the *last* of each duplicate
 862	// item.  This is needed to give the right priority when adding
 863	// duplicate items to an existing outfit.
 864	for (S32 i=items.count()-1; i>=0; i--)
 865	{
 866		LLViewerInventoryItem *item = items.get(i);
 867		LLUUID item_id = item->getLinkedUUID();
 868		if (items_seen.find(item_id)!=items_seen.end())
 869			continue;
 870		items_seen.insert(item_id);
 871		tmp_list.push_front(item);
 872	}
 873	for (std::deque<LLViewerInventoryItem*>::iterator it = tmp_list.begin();
 874		 it != tmp_list.end();
 875		 ++it)
 876	{
 877		new_items.put(*it);
 878	}
 879	items = new_items;
 880}
 881
 882const LLUUID LLAppearanceMgr::getCOF() const
 883{
 884	return gInventory.findCategoryUUIDForType(LLFolderType::FT_CURRENT_OUTFIT);
 885}
 886
 887
 888const LLViewerInventoryItem* LLAppearanceMgr::getBaseOutfitLink()
 889{
 890	const LLUUID& current_outfit_cat = getCOF();
 891	LLInventoryModel::cat_array_t cat_array;
 892	LLInventoryModel::item_array_t item_array;
 893	// Can't search on FT_OUTFIT since links to categories return FT_CATEGORY for type since they don't
 894	// return preferred type.
 895	LLIsType is_category( LLAssetType::AT_CATEGORY ); 
 896	gInventory.collectDescendentsIf(current_outfit_cat,
 897									cat_array,
 898									item_array,
 899									false,
 900									is_category,
 901									false);
 902	for (LLInventoryModel::item_array_t::const_iterator iter = item_array.begin();
 903		 iter != item_array.end();
 904		 iter++)
 905	{
 906		const LLViewerInventoryItem *item = (*iter);
 907		const LLViewerInventoryCategory *cat = item->getLinkedCategory();
 908		if (cat && cat->getPreferredType() == LLFolderType::FT_OUTFIT)
 909		{
 910			const LLUUID parent_id = cat->getParentUUID();
 911			LLViewerInventoryCategory*  parent_cat =  gInventory.getCategory(parent_id);
 912			// if base outfit moved to trash it means that we don't have base outfit
 913			if (parent_cat != NULL && parent_cat->getPreferredType() == LLFolderType::FT_TRASH)
 914			{
 915				return NULL;
 916			}
 917			return item;
 918		}
 919	}
 920	return NULL;
 921}
 922
 923bool LLAppearanceMgr::getBaseOutfitName(std::string& name)
 924{
 925	const LLViewerInventoryItem* outfit_link = getBaseOutfitLink();
 926	if(outfit_link)
 927	{
 928		const LLViewerInventoryCategory *cat = outfit_link->getLinkedCategory();
 929		if (cat)
 930		{
 931			name = cat->getName();
 932			return true;
 933		}
 934	}
 935	return false;
 936}
 937
 938const LLUUID LLAppearanceMgr::getBaseOutfitUUID()
 939{
 940	const LLViewerInventoryItem* outfit_link = getBaseOutfitLink();
 941	if (!outfit_link || !outfit_link->getIsLinkType()) return LLUUID::null;
 942
 943	const LLViewerInventoryCategory* outfit_cat = outfit_link->getLinkedCategory();
 944	if (!outfit_cat) return LLUUID::null;
 945
 946	if (outfit_cat->getPreferredType() != LLFolderType::FT_OUTFIT)
 947	{
 948		llwarns << "Expected outfit type:" << LLFolderType::FT_OUTFIT << " but got type:" << outfit_cat->getType() << " for folder name:" << outfit_cat->getName() << llendl;
 949		return LLUUID::null;
 950	}
 951
 952	return outfit_cat->getUUID();
 953}
 954
 955bool LLAppearanceMgr::wearItemOnAvatar(const LLUUID& item_id_to_wear, bool do_update, bool replace, LLPointer<LLInventoryCallback> cb)
 956{
 957	if (item_id_to_wear.isNull()) return false;
 958
 959	// *TODO: issue with multi-wearable should be fixed:
 960	// in this case this method will be called N times - loading started for each item
 961	// and than N times will be called - loading completed for each item.
 962	// That means subscribers will be notified that loading is done after first item in a batch is worn.
 963	// (loading indicator disappears for example before all selected items are worn)
 964	// Have not fix this issue for 2.1 because of stability reason. EXT-7777.
 965
 966	// Disabled for now because it is *not* acceptable to call updateAppearanceFromCOF() multiple times
 967//	gAgentWearables.notifyLoadingStarted();
 968
 969	LLViewerInventoryItem* item_to_wear = gInventory.getItem(item_id_to_wear);
 970	if (!item_to_wear) return false;
 971
 972	if (gInventory.isObjectDescendentOf(item_to_wear->getUUID(), gInventory.getLibraryRootFolderID()))
 973	{
 974		LLPointer<LLInventoryCallback> cb = new WearOnAvatarCallback(replace);
 975		copy_inventory_item(gAgent.getID(), item_to_wear->getPermissions().getOwner(), item_to_wear->getUUID(), LLUUID::null, std::string(),cb);
 976		return false;
 977	} 
 978	else if (!gInventory.isObjectDescendentOf(item_to_wear->getUUID(), gInventory.getRootFolderID()))
 979	{
 980		return false; // not in library and not in agent's inventory
 981	}
 982	else if (gInventory.isObjectDescendentOf(item_to_wear->getUUID(), gInventory.findCategoryUUIDForType(LLFolderType::FT_TRASH)))
 983	{
 984		LLNotificationsUtil::add("CannotWearTrash");
 985		return false;
 986	}
 987	else if (gInventory.isObjectDescendentOf(item_to_wear->getUUID(), LLAppearanceMgr::instance().getCOF())) // EXT-84911
 988	{
 989		return false;
 990	}
 991
 992	switch (item_to_wear->getType())
 993	{
 994	case LLAssetType::AT_CLOTHING:
 995		if (gAgentWearables.areWearablesLoaded())
 996		{
 997			S32 wearable_count = gAgentWearables.getWearableCount(item_to_wear->getWearableType());
 998			if ((replace && wearable_count != 0) ||
 999				(wearable_count >= LLAgentWearables::MAX_CLOTHING_PER_TYPE) )
1000			{
1001				removeCOFItemLinks(gAgentWearables.getWearableItemID(item_to_wear->getWearableType(), wearable_count-1), false);
1002			}
1003			addCOFItemLink(item_to_wear, do_update, cb);
1004		} 
1005		break;
1006	case LLAssetType::AT_BODYPART:
1007		// TODO: investigate wearables may not be loaded at this point EXT-8231
1008		
1009		// Remove the existing wearables of the same type.
1010		// Remove existing body parts anyway because we must not be able to wear e.g. two skins.
1011		removeCOFLinksOfType(item_to_wear->getWearableType(), false);
1012
1013		addCOFItemLink(item_to_wear, do_update, cb);
1014		break;
1015	case LLAssetType::AT_OBJECT:
1016		rez_attachment(item_to_wear, NULL, replace);
1017		break;
1018	default: return false;;
1019	}
1020
1021	return true;
1022}
1023
1024// Update appearance from outfit folder.
1025void LLAppearanceMgr::changeOutfit(bool proceed, const LLUUID& category, bool append)
1026{
1027	if (!proceed)
1028		return;
1029	LLAppearanceMgr::instance().updateCOF(category,append);
1030}
1031
1032void LLAppearanceMgr::replaceCurrentOutfit(const LLUUID& new_outfit)
1033{
1034	LLViewerInventoryCategory* cat = gInventory.getCategory(new_outfit);
1035	wearInventoryCategory(cat, false, false);
1036}
1037
1038// Open outfit renaming dialog.
1039void LLAppearanceMgr::renameOutfit(const LLUUID& outfit_id)
1040{
1041	LLViewerInventoryCategory* cat = gInventory.getCategory(outfit_id);
1042	if (!cat)
1043	{
1044		return;
1045	}
1046
1047	LLSD args;
1048	args["NAME"] = cat->getName();
1049
1050	LLSD payload;
1051	payload["cat_id"] = outfit_id;
1052
1053	LLNotificationsUtil::add("RenameOutfit", args, payload, boost::bind(onOutfitRename, _1, _2));
1054}
1055
1056// User typed new outfit name.
1057// static
1058void LLAppearanceMgr::onOutfitRename(const LLSD& notification, const LLSD& response)
1059{
1060	S32 option = LLNotificationsUtil::getSelectedOption(notification, response);
1061	if (option != 0) return; // canceled
1062
1063	std::string outfit_name = response["new_name"].asString();
1064	LLStringUtil::trim(outfit_name);
1065	if (!outfit_name.empty())
1066	{
1067		LLUUID cat_id = notification["payload"]["cat_id"].asUUID();
1068		rename_category(&gInventory, cat_id, outfit_name);
1069	}
1070}
1071
1072void LLAppearanceMgr::setOutfitLocked(bool locked)
1073{
1074	if (mOutfitLocked == locked)
1075	{
1076		return;
1077	}
1078
1079	mOutfitLocked = locked;
1080	if (locked)
1081	{
1082		mUnlockOutfitTimer->reset();
1083		mUnlockOutfitTimer->start();
1084	}
1085	else
1086	{
1087		mUnlockOutfitTimer->stop();
1088	}
1089
1090	LLOutfitObserver::instance().notifyOutfitLockChanged();
1091}
1092
1093void LLAppearanceMgr::addCategoryToCurrentOutfit(const LLUUID& cat_id)
1094{
1095	LLViewerInventoryCategory* cat = gInventory.getCategory(cat_id);
1096	wearInventoryCategory(cat, false, true);
1097}
1098
1099void LLAppearanceMgr::takeOffOutfit(const LLUUID& cat_id)
1100{
1101	LLInventoryModel::cat_array_t cats;
1102	LLInventoryModel::item_array_t items;
1103	LLFindWearablesEx collector(/*is_worn=*/ true, /*include_body_parts=*/ false);
1104
1105	gInventory.collectDescendentsIf(cat_id, cats, items, FALSE, collector);
1106
1107	LLInventoryModel::item_array_t::const_iterator it = items.begin();
1108	const LLInventoryModel::item_array_t::const_iterator it_end = items.end();
1109	for( ; it_end != it; ++it)
1110	{
1111		LLViewerInventoryItem* item = *it;
1112		removeItemFromAvatar(item->getUUID());
1113	}
1114}
1115
1116// Create a copy of src_id + contents as a subfolder of dst_id.
1117void LLAppearanceMgr::shallowCopyCategory(const LLUUID& src_id, const LLUUID& dst_id,
1118											  LLPointer<LLInventoryCallback> cb)
1119{
1120	LLInventoryCategory *src_cat = gInventory.getCategory(src_id);
1121	if (!src_cat)
1122	{
1123		llwarns << "folder not found for src " << src_id.asString() << llendl;
1124		return;
1125	}
1126	llinfos << "starting, src_id " << src_id << " name " << src_cat->getName() << " dst_id " << dst_id << llendl;
1127	LLUUID parent_id = dst_id;
1128	if(parent_id.isNull())
1129	{
1130		parent_id = gInventory.getRootFolderID();
1131	}
1132	LLUUID subfolder_id = gInventory.createNewCategory( parent_id,
1133														LLFolderType::FT_NONE,
1134														src_cat->getName());
1135	shallowCopyCategoryContents(src_id, subfolder_id, cb);
1136
1137	gInventory.notifyObservers();
1138}
1139
1140// Copy contents of src_id to dst_id.
1141void LLAppearanceMgr::shallowCopyCategoryContents(const LLUUID& src_id, const LLUUID& dst_id,
1142													  LLPointer<LLInventoryCallback> cb)
1143{
1144	LLInventoryModel::cat_array_t* cats;
1145	LLInventoryModel::item_array_t* items;
1146	gInventory.getDirectDescendentsOf(src_id, cats, items);
1147	llinfos << "copying " << items->count() << " items" << llendl;
1148	for (LLInventoryModel::item_array_t::const_iterator iter = items->begin();
1149		 iter != items->end();
1150		 ++iter)
1151	{
1152		const LLViewerInventoryItem* item = (*iter);
1153		switch (item->getActualType())
1154		{
1155			case LLAssetType::AT_LINK:
1156			{
1157				//LLInventoryItem::getDescription() is used for a new description 
1158				//to propagate ordering information saved in descriptions of links
1159				link_inventory_item(gAgent.getID(),
1160									item->getLinkedUUID(),
1161									dst_id,
1162									item->getName(),
1163									item->LLInventoryItem::getDescription(),
1164									LLAssetType::AT_LINK, cb);
1165				break;
1166			}
1167			case LLAssetType::AT_LINK_FOLDER:
1168			{
1169				LLViewerInventoryCategory *catp = item->getLinkedCategory();
1170				// Skip copying outfit links.
1171				if (catp && catp->getPreferredType() != LLFolderType::FT_OUTFIT)
1172				{
1173					link_inventory_item(gAgent.getID(),
1174										item->getLinkedUUID(),
1175										dst_id,
1176										item->getName(),
1177										item->getDescription(),
1178										LLAssetType::AT_LINK_FOLDER, cb);
1179				}
1180				break;
1181			}
1182			case LLAssetType::AT_CLOTHING:
1183			case LLAssetType::AT_OBJECT:
1184			case LLAssetType::AT_BODYPART:
1185			case LLAssetType::AT_GESTURE:
1186			{
1187				llinfos << "copying inventory item " << item->getName() << llendl;
1188				copy_inventory_item(gAgent.getID(),
1189									item->getPermissions().getOwner(),
1190									item->getUUID(),
1191									dst_id,
1192									item->getName(),
1193									cb);
1194				break;
1195			}
1196			default:
1197				// Ignore non-outfit asset types
1198				break;
1199		}
1200	}
1201}
1202
1203BOOL LLAppearanceMgr::getCanMakeFolderIntoOutfit(const LLUUID& folder_id)
1204{
1205	// These are the wearable items that are required for considering this
1206	// folder as containing a complete outfit.
1207	U32 required_wearables = 0;
1208	required_wearables |= 1LL << LLWearableType::WT_SHAPE;
1209	required_wearables |= 1LL << LLWearableType::WT_SKIN;
1210	required_wearables |= 1LL << LLWearableType::WT_HAIR;
1211	required_wearables |= 1LL << LLWearableType::WT_EYES;
1212
1213	// These are the wearables that the folder actually contains.
1214	U32 folder_wearables = 0;
1215	LLInventoryModel::cat_array_t* cats;
1216	LLInventoryModel::item_array_t* items;
1217	gInventory.getDirectDescendentsOf(folder_id, cats, items);
1218	for (LLInventoryModel::item_array_t::const_iterator iter = items->begin();
1219		 iter != items->end();
1220		 ++iter)
1221	{
1222		const LLViewerInventoryItem* item = (*iter);
1223		if (item->isWearableType())
1224		{
1225			const LLWearableType::EType wearable_type = item->getWearableType();
1226			folder_wearables |= 1LL << wearable_type;
1227		}
1228	}
1229
1230	// If the folder contains the required wearables, return TRUE.
1231	return ((required_wearables & folder_wearables) == required_wearables);
1232}
1233
1234bool LLAppearanceMgr::getCanRemoveOutfit(const LLUUID& outfit_cat_id)
1235{
1236	// Disallow removing the base outfit.
1237	if (outfit_cat_id == getBaseOutfitUUID())
1238	{
1239		return false;
1240	}
1241
1242	// Check if the outfit folder itself is removable.
1243	if (!get_is_category_removable(&gInventory, outfit_cat_id))
1244	{
1245		return false;
1246	}
1247
1248	// Check for the folder's non-removable descendants.
1249	LLFindNonRemovableObjects filter_non_removable;
1250	LLInventoryModel::cat_array_t cats;
1251	LLInventoryModel::item_array_t items;
1252	LLInventoryModel::item_array_t::const_iterator it;
1253	gInventory.collectDescendentsIf(outfit_cat_id, cats, items, false, filter_non_removable);
1254	if (!cats.empty() || !items.empty())
1255	{
1256		return false;
1257	}
1258
1259	return true;
1260}
1261
1262// static
1263bool LLAppearanceMgr::getCanRemoveFromCOF(const LLUUID& outfit_cat_id)
1264{
1265	LLInventoryModel::cat_array_t cats;
1266	LLInventoryModel::item_array_t items;
1267	LLFindWearablesEx is_worn(/*is_worn=*/ true, /*include_body_parts=*/ false);
1268	gInventory.collectDescendentsIf(outfit_cat_id,
1269		cats,
1270		items,
1271		LLInventoryModel::EXCLUDE_TRASH,
1272		is_worn);
1273	return items.size() > 0;
1274}
1275
1276// static
1277bool LLAppearanceMgr::getCanAddToCOF(const LLUUID& outfit_cat_id)
1278{
1279	if (gAgentWearables.isCOFChangeInProgress())
1280	{
1281		return false;
1282	}
1283
1284	LLInventoryModel::cat_array_t cats;
1285	LLInventoryModel::item_array_t items;
1286	LLFindWearablesEx not_worn(/*is_worn=*/ false, /*include_body_parts=*/ false);
1287	gInventory.collectDescendentsIf(outfit_cat_id,
1288		cats,
1289		items,
1290		LLInventoryModel::EXCLUDE_TRASH,
1291		not_worn);
1292	return items.size() > 0;
1293}
1294
1295bool LLAppearanceMgr::getCanReplaceCOF(const LLUUID& outfit_cat_id)
1296{
1297	// Don't allow wearing anything while we're changing appearance.
1298	if (gAgentWearables.isCOFChangeInProgress())
1299	{
1300		return false;
1301	}
1302
1303	// Check whether it's the base outfit.
1304	if (outfit_cat_id.isNull() || outfit_cat_id == getBaseOutfitUUID())
1305	{
1306		return false;
1307	}
1308
1309	// Check whether the outfit contains any wearables we aren't wearing already (STORM-702).
1310	LLInventoryModel::cat_array_t cats;
1311	LLInventoryModel::item_array_t items;
1312	LLFindWearablesEx is_worn(/*is_worn=*/ false, /*include_body_parts=*/ true);
1313	gInventory.collectDescendentsIf(outfit_cat_id,
1314		cats,
1315		items,
1316		LLInventoryModel::EXCLUDE_TRASH,
1317		is_worn);
1318	return items.size() > 0;
1319}
1320
1321void LLAppearanceMgr::purgeBaseOutfitLink(const LLUUID& category)
1322{
1323	LLInventoryModel::cat_array_t cats;
1324	LLInventoryModel::item_array_t items;
1325	gInventory.collectDescendents(category, cats, items,
1326								  LLInventoryModel::EXCLUDE_TRASH);
1327	for (S32 i = 0; i < items.count(); ++i)
1328	{
1329		LLViewerInventoryItem *item = items.get(i);
1330		if (item->getActualType() != LLAssetType::AT_LINK_FOLDER)
1331			continue;
1332		if (item->getIsLinkType())
1333		{
1334			LLViewerInventoryCategory* catp = item->getLinkedCategory();
1335			if(catp && catp->getPreferredType() == LLFolderType::FT_OUTFIT)
1336			{
1337				gInventory.purgeObject(item->getUUID());
1338			}
1339		}
1340	}
1341}
1342
1343void LLAppearanceMgr::purgeCategory(const LLUUID& category, bool keep_outfit_links)
1344{
1345	LLInventoryModel::cat_array_t cats;
1346	LLInventoryModel::item_array_t items;
1347	gInventory.collectDescendents(category, cats, items,
1348								  LLInventoryModel::EXCLUDE_TRASH);
1349	for (S32 i = 0; i < items.count(); ++i)
1350	{
1351		LLViewerInventoryItem *item = items.get(i);
1352		if (keep_outfit_links && (item->getActualType() == LLAssetType::AT_LINK_FOLDER))
1353			continue;
1354		if (item->getIsLinkType())
1355		{
1356			gInventory.purgeObject(item->getUUID());
1357		}
1358	}
1359}
1360
1361// Keep the last N wearables of each type.  For viewer 2.0, N is 1 for
1362// both body parts and clothing items.
1363void LLAppearanceMgr::filterWearableItems(
1364	LLInventoryModel::item_array_t& items, S32 max_per_type)
1365{
1366	// Divvy items into arrays by wearable type.
1367	std::vector<LLInventoryModel::item_array_t> items_by_type(LLWearableType::WT_COUNT);
1368	divvyWearablesByType(items, items_by_type);
1369
1370	// rebuild items list, retaining the last max_per_type of each array
1371	items.clear();
1372	for (S32 i=0; i<LLWearableType::WT_COUNT; i++)
1373	{
1374		S32 size = items_by_type[i].size();
1375		if (size <= 0)
1376			continue;
1377		S32 start_index = llmax(0,size-max_per_type);
1378		for (S32 j = start_index; j<size; j++)
1379		{
1380			items.push_back(items_by_type[i][j]);
1381		}
1382	}
1383}
1384
1385// Create links to all listed items.
1386void LLAppearanceMgr::linkAll(const LLUUID& cat_uuid,
1387								  LLInventoryModel::item_array_t& items,
1388								  LLPointer<LLInventoryCallback> cb)
1389{
1390	for (S32 i=0; i<items.count(); i++)
1391	{
1392		const LLInventoryItem* item = items.get(i).get();
1393		link_inventory_item(gAgent.getID(),
1394							item->getLinkedUUID(),
1395							cat_uuid,
1396							item->getName(),
1397							item->LLInventoryItem::getDescription(),
1398							LLAssetType::AT_LINK,
1399							cb);
1400
1401		const LLViewerInventoryCategory *cat = gInventory.getCategory(cat_uuid);
1402		const std::string cat_name = cat ? cat->getName() : "CAT NOT FOUND";
1403#ifndef LL_RELEASE_FOR_DOWNLOAD
1404		llinfos << "Linking Item [ name:" << item->getName() << " UUID:" << item->getUUID() << " ] to Category [ name:" << cat_name << " UUID:" << cat_uuid << " ] " << llendl;
1405#endif
1406	}
1407}
1408
1409void LLAppearanceMgr::updateCOF(const LLUUID& category, bool append)
1410{
1411	LLViewerInventoryCategory *pcat = gInventory.getCategory(category);
1412	llinfos << "starting, cat " << (pcat ? pcat->getName() : "[UNKNOWN]") << llendl;
1413
1414	const LLUUID cof = getCOF();
1415
1416	// Deactivate currently active gestures in the COF, if replacing outfit
1417	if (!append)
1418	{
1419		LLInventoryModel::item_array_t gest_items;
1420		getDescendentsOfAssetType(cof, gest_items, LLAssetType::AT_GESTURE, false);
1421		for(S32 i = 0; i  < gest_items.count(); ++i)
1422		{
1423			LLViewerInventoryItem *gest_item = gest_items.get(i);
1424			if ( LLGestureMgr::instance().isGestureActive( gest_item->getLinkedUUID()) )
1425			{
1426				LLGestureMgr::instance().deactivateGesture( gest_item->getLinkedUUID() );
1427			}
1428		}
1429	}
1430	
1431	// Collect and filter descendents to determine new COF contents.
1432
1433	// - Body parts: always include COF contents as a fallback in case any
1434	// required parts are missing.
1435	// Preserve body parts from COF if appending.
1436	LLInventoryModel::item_array_t body_items;
1437	getDescendentsOfAssetType(cof, body_items, LLAssetType::AT_BODYPART, false);
1438	getDescendentsOfAssetType(category, body_items, LLAssetType::AT_BODYPART, false);
1439	if (append)
1440		reverse(body_items.begin(), body_items.end());
1441	// Reduce body items to max of one per type.
1442	removeDuplicateItems(body_items);
1443	filterWearableItems(body_items, 1);
1444
1445	// - Wearables: include COF contents only if appending.
1446	LLInventoryModel::item_array_t wear_items;
1447	if (append)
1448		getDescendentsOfAssetType(cof, wear_items, LLAssetType::AT_CLOTHING, false);
1449	getDescendentsOfAssetType(category, wear_items, LLAssetType::AT_CLOTHING, false);
1450	// Reduce wearables to max of one per type.
1451	removeDuplicateItems(wear_items);
1452	filterWearableItems(wear_items, LLAgentWearables::MAX_CLOTHING_PER_TYPE);
1453
1454	// - Attachments: include COF contents only if appending.
1455	LLInventoryModel::item_array_t obj_items;
1456	if (append)
1457		getDescendentsOfAssetType(cof, obj_items, LLAssetType::AT_OBJECT, false);
1458	getDescendentsOfAssetType(category, obj_items, LLAssetType::AT_OBJECT, false);
1459	removeDuplicateItems(obj_items);
1460
1461	// - Gestures: include COF contents only if appending.
1462	LLInventoryModel::item_array_t gest_items;
1463	if (append)
1464		getDescendentsOfAssetType(cof, gest_items, LLAssetType::AT_GESTURE, false);
1465	getDescendentsOfAssetType(category, gest_items, LLAssetType::AT_GESTURE, false);
1466	removeDuplicateItems(gest_items);
1467	
1468	// Remove current COF contents.
1469	bool keep_outfit_links = append;
1470	purgeCategory(cof, keep_outfit_links);
1471	gInventory.notifyObservers();
1472
1473	// Create links to new COF contents.
1474	llinfos << "creating LLUpdateAppearanceOnDestroy" << llendl;
1475	LLPointer<LLInventoryCallback> link_waiter = new LLUpdateAppearanceOnDestroy(!append);
1476
1477#ifndef LL_RELEASE_FOR_DOWNLOAD
1478	llinfos << "Linking body items" << llendl;
1479#endif
1480	linkAll(cof, body_items, link_waiter);
1481
1482#ifndef LL_RELEASE_FOR_DOWNLOAD
1483	llinfos << "Linking wear items" << llendl;
1484#endif
1485	linkAll(cof, wear_items, link_waiter);
1486
1487#ifndef LL_RELEASE_FOR_DOWNLOAD
1488	llinfos << "Linking obj items" << llendl;
1489#endif
1490	linkAll(cof, obj_items, link_waiter);
1491
1492#ifndef LL_RELEASE_FOR_DOWNLOAD
1493	llinfos << "Linking gesture items" << llendl;
1494#endif
1495	linkAll(cof, gest_items, link_waiter);
1496
1497	// Add link to outfit if category is an outfit. 
1498	if (!append)
1499	{
1500		createBaseOutfitLink(category, link_waiter);
1501	}
1502	llinfos << "waiting for LLUpdateAppearanceOnDestroy" << llendl;
1503}
1504
1505void LLAppearanceMgr::updatePanelOutfitName(const std::string& name)
1506{
1507	LLSidepanelAppearance* panel_appearance =
1508		dynamic_cast<LLSidepanelAppearance *>(LLFloaterSidePanelContainer::getPanel("appearance"));
1509	if (panel_appearance)
1510	{
1511		panel_appearance->refreshCurrentOutfitName(name);
1512	}
1513}
1514
1515void LLAppearanceMgr::createBaseOutfitLink(const LLUUID& category, LLPointer<LLInventoryCallback> link_waiter)
1516{
1517	const LLUUID cof = getCOF();
1518	LLViewerInventoryCategory* catp = gInventory.getCategory(category);
1519	std::string new_outfit_name = "";
1520
1521	purgeBaseOutfitLink(cof);
1522
1523	if (catp && catp->getPreferredType() == LLFolderType::FT_OUTFIT)
1524	{
1525		link_inventory_item(gAgent.getID(), category, cof, catp->getName(), "",
1526							LLAssetType::AT_LINK_FOLDER, link_waiter);
1527		new_outfit_name = catp->getName();
1528	}
1529	
1530	updatePanelOutfitName(new_outfit_name);
1531}
1532
1533void LLAppearanceMgr::updateAgentWearables(LLWearableHoldingPattern* holder, bool append)
1534{
1535	lldebugs << "updateAgentWearables()" << llendl;
1536	LLInventoryItem::item_array_t items;
1537	LLDynamicArray< LLWearable* > wearables;
1538
1539	// For each wearable type, find the wearables of that type.
1540	for( S32 i = 0; i < LLWearableType::WT_COUNT; i++ )
1541	{
1542		for (LLWearableHoldingPattern::found_list_t::iterator iter = holder->getFoundList().begin();
1543			 iter != holder->getFoundList().end(); ++iter)
1544		{
1545			LLFoundData& data = *iter;
1546			LLWearable* wearable = data.mWearable;
1547			if( wearable && ((S32)wearable->getType() == i) )
1548			{
1549				LLViewerInventoryItem* item = (LLViewerInventoryItem*)gInventory.getItem(data.mItemID);
1550				if( item && (item->getAssetUUID() == wearable->getAssetID()) )
1551				{
1552					items.put(item);
1553					wearables.put(wearable);
1554				}
1555			}
1556		}
1557	}
1558
1559	if(wearables.count() > 0)
1560	{
1561		gAgentWearables.setWearableOutfit(items, wearables, !append);
1562	}
1563
1564//	dec_busy_count();
1565}
1566
1567static void remove_non_link_items(LLInventoryModel::item_array_t &items)
1568{
1569	LLInventoryModel::item_array_t pruned_items;
1570	for (LLInventoryModel::item_array_t::const_iterator iter = items.begin();
1571		 iter != items.end();
1572		 ++iter)
1573	{
1574 		const LLViewerInventoryItem *item = (*iter);
1575		if (item && item->getIsLinkType())
1576		{
1577			pruned_items.push_back((*iter));
1578		}
1579	}
1580	items = pruned_items;
1581}
1582
1583//a predicate for sorting inventory items by actual descriptions
1584bool sort_by_description(const LLInventoryItem* item1, const LLInventoryItem* item2)
1585{
1586	if (!item1 || !item2) 
1587	{
1588		llwarning("either item1 or item2 is NULL", 0);
1589		return true;
1590	}
1591
1592	return item1->LLInventoryItem::getDescription() < item2->LLInventoryItem::getDescription();
1593}
1594
1595void item_array_diff(LLInventoryModel::item_array_t& full_list,
1596					 LLInventoryModel::item_array_t& keep_list,
1597					 LLInventoryModel::item_array_t& kill_list)
1598	
1599{
1600	for (LLInventoryModel::item_array_t::iterator it = full_list.begin();
1601		 it != full_list.end();
1602		 ++it)
1603	{
1604		LLViewerInventoryItem *item = *it;
1605		if (keep_list.find(item) < 0) // Why on earth does LLDynamicArray need to redefine find()?
1606		{
1607			kill_list.push_back(item);
1608		}
1609	}
1610}
1611
1612S32 LLAppearanceMgr::findExcessOrDuplicateItems(const LLUUID& cat_id,
1613												 LLAssetType::EType type,
1614												 S32 max_items,
1615												 LLInventoryModel::item_array_t& items_to_kill)
1616{
1617	S32 to_kill_count = 0;
1618
1619	LLInventoryModel::item_array_t items;
1620	getDescendentsOfAssetType(cat_id, items, type, false);
1621	LLInventoryModel::item_array_t curr_items = items;
1622	removeDuplicateItems(items);
1623	if (max_items > 0)
1624	{
1625		filterWearableItems(items, max_items);
1626	}
1627	LLInventoryModel::item_array_t kill_items;
1628	item_array_diff(curr_items,items,kill_items);
1629	for (LLInventoryModel::item_array_t::iterator it = kill_items.begin();
1630		 it != kill_items.end();
1631		 ++it)
1632	{
1633		items_to_kill.push_back(*it);
1634		to_kill_count++;
1635	}
1636	return to_kill_count;
1637}
1638	
1639												 
1640void LLAppearanceMgr::enforceItemRestrictions()
1641{
1642	S32 purge_count = 0;
1643	LLInventoryModel::item_array_t items_to_kill;
1644
1645	purge_count += findExcessOrDuplicateItems(getCOF(),LLAssetType::AT_BODYPART,
1646											  1, items_to_kill);
1647	purge_count += findExcessOrDuplicateItems(getCOF(),LLAssetType::AT_CLOTHING,
1648											  LLAgentWearables::MAX_CLOTHING_PER_TYPE, items_to_kill);
1649	purge_count += findExcessOrDuplicateItems(getCOF(),LLAssetType::AT_OBJECT,
1650											  -1, items_to_kill);
1651
1652	if (items_to_kill.size()>0)
1653	{
1654		for (LLInventoryModel::item_array_t::iterator it = items_to_kill.begin();
1655			 it != items_to_kill.end();
1656			 ++it)
1657		{
1658			LLViewerInventoryItem *item = *it;
1659			llinfos << "purging duplicate or excess item " << item->getName() << llendl;
1660			gInventory.purgeObject(item->getUUID());
1661		}
1662		gInventory.notifyObservers();
1663	}
1664}
1665
1666void LLAppearanceMgr::updateAppearanceFromCOF(bool update_base_outfit_ordering)
1667{
1668	if (mIsInUpdateAppearanceFromCOF)
1669	{
1670		llwarns << "Called updateAppearanceFromCOF inside updateAppearanceFromCOF, skipping" << llendl;
1671		return;
1672	}
1673
1674	BoolSetter setIsInUpdateAppearanceFromCOF(mIsInUpdateAppearanceFromCOF);
1675
1676	llinfos << "starting" << llendl;
1677
1678	//checking integrity of the COF in terms of ordering of wearables, 
1679	//checking and updating links' descriptions of wearables in the COF (before analyzed for "dirty" state)
1680	updateClothingOrderingInfo(LLUUID::null, update_base_outfit_ordering);
1681
1682	// Remove duplicate or excess wearables. Should normally be enforced at the UI level, but
1683	// this should catch anything that gets through.
1684	enforceItemRestrictions();
1685	
1686	// update dirty flag to see if the state of the COF matches
1687	// the saved outfit stored as a folder link
1688	updateIsDirty();
1689
1690	//dumpCat(getCOF(),"COF, start");
1691
1692	bool follow_folder_links 

Large files files are truncated, but you can click here to view the full file