From 73d152093dd5bb31ad264afdd3dc8e283dff49e3 Mon Sep 17 00:00:00 2001
From: Bradley Payne <vir@lindenlab.com>
Date: Thu, 10 Sep 2009 18:42:19 +0000
Subject: [PATCH] For DEV-34223: Avatar Pipeline Project - M6 (Current Outfit
 Folder, Appearance Side Panel) - brought merge branch changes from
 avatar-pipeline/viewer-2.0.0-3_cwf-7_merge into viewer-2.0.0-3.

svn merge -r132878:132947 svn+ssh://svn.lindenlab.com/svn/linden/branches/avatar-pipeline/viewer-2.0.0-3_cwf-7_merge
---
 indra/llcommon/llassettype.cpp      |  26 +-
 indra/newview/CMakeLists.txt        |   2 +
 indra/newview/llagentwearables.cpp  | 138 +++--
 indra/newview/llagentwearables.h    |   1 +
 indra/newview/llappearancemgr.cpp   | 793 +++++++++++++++++++++++++
 indra/newview/llappearancemgr.h     |  70 +++
 indra/newview/llfolderviewitem.cpp  |   6 +-
 indra/newview/llinventorybridge.cpp | 879 +++++-----------------------
 indra/newview/llinventorybridge.h   |   9 +-
 indra/newview/llinventoryfilter.h   |   1 +
 indra/newview/llinventorymodel.cpp  |  55 +-
 indra/newview/llinventorymodel.h    |  28 +-
 indra/newview/llstartup.cpp         |   7 +-
 indra/newview/lltooldraganddrop.cpp |   5 +-
 indra/newview/llviewerinventory.cpp |  40 +-
 indra/newview/llviewerinventory.h   |   9 +-
 indra/newview/llviewermenu.cpp      |   5 +-
 17 files changed, 1212 insertions(+), 862 deletions(-)
 create mode 100644 indra/newview/llappearancemgr.cpp
 create mode 100644 indra/newview/llappearancemgr.h

diff --git a/indra/llcommon/llassettype.cpp b/indra/llcommon/llassettype.cpp
index e595cc1f8ba..5d7672b3780 100644
--- a/indra/llcommon/llassettype.cpp
+++ b/indra/llcommon/llassettype.cpp
@@ -80,27 +80,27 @@ LLAssetDictionary::LLAssetDictionary()
 {
 	//       												   DESCRIPTION			TYPE NAME	HUMAN NAME			CATEGORY NAME 		DRAG&DROP		CAN LINK?	PROTECTED?
 	//      												  |--------------------|-----------|-------------------|-------------------|---------------|-----------|-----------|
-	addEntry(LLAssetType::AT_TEXTURE, 			new AssetEntry("TEXTURE",			"texture",	"texture",			"Textures", 		DAD_TEXTURE,	FALSE,		TRUE));
-	addEntry(LLAssetType::AT_SOUND, 			new AssetEntry("SOUND",				"sound",	"sound",			"Sounds", 			DAD_SOUND,		FALSE,		TRUE));
-	addEntry(LLAssetType::AT_CALLINGCARD, 		new AssetEntry("CALLINGCARD",		"callcard",	"calling card",		"Calling Cards", 	DAD_CALLINGCARD, FALSE,		TRUE));
-	addEntry(LLAssetType::AT_LANDMARK, 			new AssetEntry("LANDMARK",			"landmark",	"landmark",			"Landmarks", 		DAD_LANDMARK,	FALSE,		TRUE));
-	addEntry(LLAssetType::AT_SCRIPT, 			new AssetEntry("SCRIPT",			"script",	"legacy script",	"Scripts", 			DAD_NONE,		FALSE,		TRUE));
+	addEntry(LLAssetType::AT_TEXTURE, 			new AssetEntry("TEXTURE",			"texture",	"texture",			"Textures", 		DAD_TEXTURE,	TRUE,		TRUE));
+	addEntry(LLAssetType::AT_SOUND, 			new AssetEntry("SOUND",				"sound",	"sound",			"Sounds", 			DAD_SOUND,		TRUE,		TRUE));
+	addEntry(LLAssetType::AT_CALLINGCARD, 		new AssetEntry("CALLINGCARD",		"callcard",	"calling card",		"Calling Cards", 	DAD_CALLINGCARD, TRUE,		TRUE));
+	addEntry(LLAssetType::AT_LANDMARK, 			new AssetEntry("LANDMARK",			"landmark",	"landmark",			"Landmarks", 		DAD_LANDMARK,	TRUE,		TRUE));
+	addEntry(LLAssetType::AT_SCRIPT, 			new AssetEntry("SCRIPT",			"script",	"legacy script",	"Scripts", 			DAD_NONE,		TRUE,		TRUE));
 	addEntry(LLAssetType::AT_CLOTHING, 			new AssetEntry("CLOTHING",			"clothing",	"clothing",			"Clothing", 		DAD_CLOTHING,	TRUE,		TRUE));
 	addEntry(LLAssetType::AT_OBJECT, 			new AssetEntry("OBJECT",			"object",	"object",			"Objects", 			DAD_OBJECT,		TRUE,		TRUE));
-	addEntry(LLAssetType::AT_NOTECARD, 			new AssetEntry("NOTECARD",			"notecard",	"note card",		"Notecards", 		DAD_NOTECARD,	FALSE,		TRUE));
+	addEntry(LLAssetType::AT_NOTECARD, 			new AssetEntry("NOTECARD",			"notecard",	"note card",		"Notecards", 		DAD_NOTECARD,	TRUE,		TRUE));
 	addEntry(LLAssetType::AT_CATEGORY, 			new AssetEntry("CATEGORY",			"category",	"folder",			"New Folder", 		DAD_CATEGORY,	TRUE,		TRUE));
 	addEntry(LLAssetType::AT_ROOT_CATEGORY, 	new AssetEntry("ROOT_CATEGORY",		"root",		"root",				"Inventory", 		DAD_ROOT_CATEGORY, TRUE,	TRUE));
-	addEntry(LLAssetType::AT_LSL_TEXT, 			new AssetEntry("LSL_TEXT",			"lsltext",	"lsl2 script",		"Scripts", 			DAD_SCRIPT,		FALSE,		TRUE));
-	addEntry(LLAssetType::AT_LSL_BYTECODE, 		new AssetEntry("LSL_BYTECODE",		"lslbyte",	"lsl bytecode",		"Scripts", 			DAD_NONE,		FALSE,		TRUE));
-	addEntry(LLAssetType::AT_TEXTURE_TGA, 		new AssetEntry("TEXTURE_TGA",		"txtr_tga",	"tga texture",		"Uncompressed Images", DAD_NONE,	FALSE,		TRUE));
+	addEntry(LLAssetType::AT_LSL_TEXT, 			new AssetEntry("LSL_TEXT",			"lsltext",	"lsl2 script",		"Scripts", 			DAD_SCRIPT,		TRUE,		TRUE));
+	addEntry(LLAssetType::AT_LSL_BYTECODE, 		new AssetEntry("LSL_BYTECODE",		"lslbyte",	"lsl bytecode",		"Scripts", 			DAD_NONE,		TRUE,		TRUE));
+	addEntry(LLAssetType::AT_TEXTURE_TGA, 		new AssetEntry("TEXTURE_TGA",		"txtr_tga",	"tga texture",		"Uncompressed Images", DAD_NONE,	TRUE,		TRUE));
 	addEntry(LLAssetType::AT_BODYPART, 			new AssetEntry("BODYPART",			"bodypart",	"body part",		"Body Parts", 		DAD_BODYPART,	TRUE,		TRUE));
 	addEntry(LLAssetType::AT_TRASH, 			new AssetEntry("TRASH",				"trash",	"trash",			"Trash", 			DAD_NONE,		FALSE,		TRUE));
 	addEntry(LLAssetType::AT_SNAPSHOT_CATEGORY, new AssetEntry("SNAPSHOT_CATEGORY", "snapshot",	"snapshot",			"Photo Album", 		DAD_NONE,		FALSE,		TRUE));
 	addEntry(LLAssetType::AT_LOST_AND_FOUND, 	new AssetEntry("LOST_AND_FOUND", 	"lstndfnd",	"lost and found",	"Lost And Found", 	DAD_NONE,		FALSE,		TRUE));
-	addEntry(LLAssetType::AT_SOUND_WAV, 		new AssetEntry("SOUND_WAV",			"snd_wav",	"sound",			"Uncompressed SoundS", DAD_NONE,	FALSE,		TRUE));
-	addEntry(LLAssetType::AT_IMAGE_TGA, 		new AssetEntry("IMAGE_TGA",			"img_tga",	"targa image",		"Uncompressed Images", DAD_NONE,	FALSE,		TRUE));
-	addEntry(LLAssetType::AT_IMAGE_JPEG, 		new AssetEntry("IMAGE_JPEG",		"jpeg",		"jpeg image",		"Uncompressed Images", DAD_NONE,	FALSE,		TRUE));
-	addEntry(LLAssetType::AT_ANIMATION, 		new AssetEntry("ANIMATION",			"animatn",	"animation",		"Animations", 		DAD_ANIMATION,	FALSE,		TRUE));
+	addEntry(LLAssetType::AT_SOUND_WAV, 		new AssetEntry("SOUND_WAV",			"snd_wav",	"sound",			"Uncompressed SoundS", DAD_NONE,	TRUE,		TRUE));
+	addEntry(LLAssetType::AT_IMAGE_TGA, 		new AssetEntry("IMAGE_TGA",			"img_tga",	"targa image",		"Uncompressed Images", DAD_NONE,	TRUE,		TRUE));
+	addEntry(LLAssetType::AT_IMAGE_JPEG, 		new AssetEntry("IMAGE_JPEG",		"jpeg",		"jpeg image",		"Uncompressed Images", DAD_NONE,	TRUE,		TRUE));
+	addEntry(LLAssetType::AT_ANIMATION, 		new AssetEntry("ANIMATION",			"animatn",	"animation",		"Animations", 		DAD_ANIMATION,	TRUE,		TRUE));
 	addEntry(LLAssetType::AT_GESTURE, 			new AssetEntry("GESTURE",			"gesture",	"gesture",			"Gestures", 		DAD_GESTURE,	TRUE,		TRUE));
 	addEntry(LLAssetType::AT_SIMSTATE, 			new AssetEntry("SIMSTATE",			"simstate",	"simstate",			"New Folder", 		DAD_NONE,		FALSE,		TRUE));
 	addEntry(LLAssetType::AT_FAVORITE, 			new AssetEntry("FAVORITE",			"favorite",	"favorite",			"favorite", 		DAD_NONE,		FALSE,		TRUE));
diff --git a/indra/newview/CMakeLists.txt b/indra/newview/CMakeLists.txt
index 321a238f704..96aace9e2d1 100644
--- a/indra/newview/CMakeLists.txt
+++ b/indra/newview/CMakeLists.txt
@@ -74,6 +74,7 @@ set(viewer_SOURCE_FILES
     llagentui.cpp
     llagentwearables.cpp
     llanimstatelabels.cpp
+    llappearancemgr.cpp
     llappviewer.cpp
     llassetuploadresponders.cpp
     llassetuploadqueue.cpp
@@ -532,6 +533,7 @@ set(viewer_HEADER_FILES
     llassetuploadresponders.h
     llassetuploadqueue.h
     llaudiosourcevo.h
+    llappearancemgr.h
     llavataractions.h
     llavatariconctrl.h
     llavatarlist.h
diff --git a/indra/newview/llagentwearables.cpp b/indra/newview/llagentwearables.cpp
index 0f735282cb0..54b94482de3 100644
--- a/indra/newview/llagentwearables.cpp
+++ b/indra/newview/llagentwearables.cpp
@@ -45,6 +45,7 @@
 #include "llwearable.h"
 #include "llwearablelist.h"
 #include "llgesturemgr.h"
+#include "llappearancemgr.h"
 
 #include <boost/scoped_ptr.hpp>
 
@@ -76,11 +77,9 @@ class LLInitialWearablesFetch : public LLInventoryFetchDescendentsObserver
 	initial_wearable_data_vec_t mAgentInitialWearables; // Wearables from the old agent wearables msg
 
 protected:
-	void processInitialWearables();
+	void processWearablesMessage();
 };
 
-
-
 LLAgentWearables gAgentWearables;
 
 BOOL LLAgentWearables::mInitialWearablesUpdateReceived = FALSE;
@@ -737,7 +736,7 @@ void LLAgentWearables::processAgentInitialWearablesUpdate(LLMessageSystem* mesgs
 		gAgentWearables.mItemsAwaitingWearableUpdate.clear();
 		for (S32 i=0; i < num_wearables; i++)
 		{
-			// Parse initial werables data from message system
+			// Parse initial wearables data from message system
 			U8 type_u8 = 0;
 			gMessageSystem->getU8Fast(_PREHASH_WearableData, _PREHASH_WearableType, type_u8, i);
 			if (type_u8 >= WT_COUNT)
@@ -1195,7 +1194,7 @@ LLUUID LLAgentWearables::makeNewOutfitLinks(const std::string& new_folder_name,
 					if (!item) continue;
 					LLPointer<LLInventoryCallback> cb = NULL;
 					link_inventory_item(gAgent.getID(),
-										item->getUUID(),
+										item->getLinkedUUID(),
 										folder_id,
 										item->getName(),
 										LLAssetType::AT_LINK,
@@ -1226,7 +1225,7 @@ LLUUID LLAgentWearables::makeNewOutfitLinks(const std::string& new_folder_name,
 
 			LLPointer<LLInventoryCallback> cb = NULL;
 			link_inventory_item(gAgent.getID(),
-								item->getUUID(),
+								item->getLinkedUUID(),
 								folder_id,
 								item->getName(),
 								LLAssetType::AT_LINK,
@@ -1781,6 +1780,57 @@ void LLAgentWearables::userRemoveAllAttachments(void* userdata)
 	gMessageSystem->sendReliable(gAgent.getRegionHost());
 }
 
+void LLAgentWearables::userAttachMultipleAttachments(LLInventoryModel::item_array_t& obj_item_array)
+{
+	// Build a compound message to send all the objects that need to be rezzed.
+	S32 obj_count = obj_item_array.count();
+
+	// Limit number of packets to send
+	const S32 MAX_PACKETS_TO_SEND = 10;
+	const S32 OBJECTS_PER_PACKET = 4;
+	const S32 MAX_OBJECTS_TO_SEND = MAX_PACKETS_TO_SEND * OBJECTS_PER_PACKET;
+	if( obj_count > MAX_OBJECTS_TO_SEND )
+	{
+		obj_count = MAX_OBJECTS_TO_SEND;
+	}
+				
+	// Create an id to keep the parts of the compound message together
+	LLUUID compound_msg_id;
+	compound_msg_id.generate();
+	LLMessageSystem* msg = gMessageSystem;
+
+	for(S32 i = 0; i < obj_count; ++i)
+	{
+		if( 0 == (i % OBJECTS_PER_PACKET) )
+		{
+			// Start a new message chunk
+			msg->newMessageFast(_PREHASH_RezMultipleAttachmentsFromInv);
+			msg->nextBlockFast(_PREHASH_AgentData);
+			msg->addUUIDFast(_PREHASH_AgentID, gAgent.getID());
+			msg->addUUIDFast(_PREHASH_SessionID, gAgent.getSessionID());
+			msg->nextBlockFast(_PREHASH_HeaderData);
+			msg->addUUIDFast(_PREHASH_CompoundMsgID, compound_msg_id );
+			msg->addU8Fast(_PREHASH_TotalObjects, obj_count );
+			msg->addBOOLFast(_PREHASH_FirstDetachAll, true );
+		}
+
+		const LLInventoryItem* item = obj_item_array.get(i).get();
+		msg->nextBlockFast(_PREHASH_ObjectData );
+		msg->addUUIDFast(_PREHASH_ItemID, item->getLinkedUUID());
+		msg->addUUIDFast(_PREHASH_OwnerID, item->getPermissions().getOwner());
+		msg->addU8Fast(_PREHASH_AttachmentPt, 0 );	// Wear at the previous or default attachment point
+		pack_permissions_slam(msg, item->getFlags(), item->getPermissions());
+		msg->addStringFast(_PREHASH_Name, item->getName());
+		msg->addStringFast(_PREHASH_Description, item->getDescription());
+
+		if( (i+1 == obj_count) || ((OBJECTS_PER_PACKET-1) == (i % OBJECTS_PER_PACKET)) )
+		{
+			// End of message chunk
+			msg->sendReliable( gAgent.getRegion()->getHost() );
+		}
+	}
+}
+
 void LLAgentWearables::checkWearablesLoaded() const
 {
 #ifdef SHOW_ASSERT
@@ -1812,69 +1862,29 @@ void LLAgentWearables::updateServer()
 
 void LLInitialWearablesFetch::done()
 {
-	// Get the complete information on the items in the library, 
-	// and set up an observer that will wait for that to happen.
-	LLInventoryModel::cat_array_t cat_array;
-	LLInventoryModel::item_array_t item_array;
+	// No longer need this observer hanging around.
+	gInventory.removeObserver(this);
 
+	// Fetch the wearable items from the Current Outfit Folder
+	LLInventoryModel::cat_array_t cat_array;
+	LLInventoryModel::item_array_t wearable_array;
 	LLFindWearables is_wearable;
-	gInventory.collectDescendentsIf(mCompleteFolders.front(),
-									cat_array,
-									item_array,
-									LLInventoryModel::EXCLUDE_TRASH,
-									is_wearable);
-	S32 count = item_array.count();
-	mCOFInitialWearables.reserve(count);
+	gInventory.collectDescendentsIf(mCompleteFolders.front(), cat_array, wearable_array, 
+									LLInventoryModel::EXCLUDE_TRASH, is_wearable);
 	
-	for (LLInventoryModel::item_array_t::const_iterator iter = item_array.begin();
-		 iter != item_array.end();
-		 iter++)
-	{
-		const LLViewerInventoryItem *item = (*iter).get();
-		// We're only concerned with linked items in the COF.  Ignore
-		// any non-link items or links to folders.
-		if (item->getActualType() != LLAssetType::AT_LINK)
-		{
-			continue;
-		}
-		EWearableType type = (EWearableType) (item->getFlags() & LLInventoryItem::II_FLAGS_WEARABLES_MASK);
-		// MULTI-WEARABLE: update
-		InitialWearableData wearable_data(type, 0, item->getUUID(), item->getAssetUUID());
-		mCOFInitialWearables.push_back(wearable_data);
+	if (wearable_array.count() > 0)
+	{
+		LLAppearanceManager::instance().updateAppearanceFromCOF();
+	}
+	else
+	{
+		processWearablesMessage();
 	}
-	
-	gInventory.removeObserver(this);
-	processInitialWearables();
 	delete this;
 }
 
-// This will either grab the contents of the Current Outfit Folder if they exist,
-// or use the old-style initial agent wearables message.  
-void LLInitialWearablesFetch::processInitialWearables()
+void LLInitialWearablesFetch::processWearablesMessage()
 {
-#ifdef USE_CURRENT_OUTFIT_FOLDER
-	if (!mCOFInitialWearables.empty())
-	{
-		for (U8 i = 0; i < mCOFInitialWearables.size(); ++i)
-		{
-			// Fetch the wearables in the current outfit folder
-			InitialWearableData *wearable_data = new InitialWearableData(mCOFInitialWearables[i]); // This will be deleted in the callback.
-			if (wearable_data->mAssetID.notNull())
-			{
-				LLWearableList::instance().getAsset(wearable_data->mAssetID,
-													LLStringUtil::null,
-													LLWearableDictionary::getAssetType(wearable_data->mType),
-													LLAgentWearables::onInitialWearableAssetArrived, (void*)(wearable_data));			
-			}
-			else
-			{
-				llinfos << "Invalid wearable, type " << wearable_data->mType << " itemID "
-						<< wearable_data->mItemID << " assetID " << wearable_data->mAssetID << llendl;
-			}
-		}
-	}
-	else 
-#endif
 	if (!mAgentInitialWearables.empty()) // We have an empty current outfit folder, use the message data instead.
 	{
 		LLUUID current_outfit_id = gInventory.findCategoryUUIDForType(LLAssetType::AT_CURRENT_OUTFIT);
@@ -1882,7 +1892,7 @@ void LLInitialWearablesFetch::processInitialWearables()
 		{
 			// Populate the current outfit folder with links to the wearables passed in the message
 			InitialWearableData *wearable_data = new InitialWearableData(mAgentInitialWearables[i]); // This will be deleted in the callback.
-
+			
 			if (wearable_data->mAssetID.notNull())
 			{
 #ifdef USE_CURRENT_OUTFIT_FOLDER
@@ -1899,7 +1909,7 @@ void LLInitialWearablesFetch::processInitialWearables()
 			else
 			{
 				llinfos << "Invalid wearable, type " << wearable_data->mType << " itemID "
-						<< wearable_data->mItemID << " assetID " << wearable_data->mAssetID << llendl;
+				<< wearable_data->mItemID << " assetID " << wearable_data->mAssetID << llendl;
 			}
 		}
 	}
@@ -1908,3 +1918,5 @@ void LLInitialWearablesFetch::processInitialWearables()
 		LL_WARNS("Wearables") << "No current outfit folder items found and no initial wearables fallback message received." << LL_ENDL;
 	}
 }
+
+
diff --git a/indra/newview/llagentwearables.h b/indra/newview/llagentwearables.h
index b415ef9eb35..cb4de555d56 100644
--- a/indra/newview/llagentwearables.h
+++ b/indra/newview/llagentwearables.h
@@ -182,6 +182,7 @@ class LLAgentWearables
 	static void		userRemoveWearable(void* userdata);	// userdata is EWearableType
 	static void		userRemoveAllClothes(void* userdata);	// userdata is NULL
 	static void		userRemoveAllAttachments(void* userdata);	// userdata is NULL 
+	static void		userAttachMultipleAttachments(LLInventoryModel::item_array_t& obj_item_array);
 
 	BOOL			itemUpdatePending(const LLUUID& item_id) const;
 	U32				itemUpdatePendingCount() const;
diff --git a/indra/newview/llappearancemgr.cpp b/indra/newview/llappearancemgr.cpp
new file mode 100644
index 00000000000..6c234f23fe8
--- /dev/null
+++ b/indra/newview/llappearancemgr.cpp
@@ -0,0 +1,793 @@
+/** 
+ * @file llappearancemgr.cpp
+ * @brief Manager for initiating appearance changes on the viewer
+ *
+ * $LicenseInfo:firstyear=2004&license=viewergpl$
+ * 
+ * Copyright (c) 2004-2009, Linden Research, Inc.
+ * 
+ * Second Life Viewer Source Code
+ * The source code in this file ("Source Code") is provided by Linden Lab
+ * to you under the terms of the GNU General Public License, version 2.0
+ * ("GPL"), unless you have obtained a separate licensing agreement
+ * ("Other License"), formally executed by you and Linden Lab.  Terms of
+ * the GPL can be found in doc/GPL-license.txt in this distribution, or
+ * online at http://secondlifegrid.net/programs/open_source/licensing/gplv2
+ * 
+ * There are special exceptions to the terms and conditions of the GPL as
+ * it is applied to this Source Code. View the full text of the exception
+ * in the file doc/FLOSS-exception.txt in this software distribution, or
+ * online at
+ * http://secondlifegrid.net/programs/open_source/licensing/flossexception
+ * 
+ * By copying, modifying or distributing this software, you acknowledge
+ * that you have read and understood your obligations described above,
+ * and agree to abide by those obligations.
+ * 
+ * ALL LINDEN LAB SOURCE CODE IS PROVIDED "AS IS." LINDEN LAB MAKES NO
+ * WARRANTIES, EXPRESS, IMPLIED OR OTHERWISE, REGARDING ITS ACCURACY,
+ * COMPLETENESS OR PERFORMANCE.
+ * $/LicenseInfo$
+ */
+
+#include "llviewerprecompiledheaders.h"
+
+#include "llappearancemgr.h"
+#include "llinventorymodel.h"
+#include "llnotifications.h"
+#include "llgesturemgr.h"
+#include "llinventorybridge.h"
+#include "llwearablelist.h"
+#include "llagentwearables.h"
+#include "llagent.h"
+#include "llvoavatar.h"
+#include "llvoavatarself.h"
+#include "llviewerregion.h"
+#include "llfloatercustomize.h"
+
+class LLWearInventoryCategoryCallback : public LLInventoryCallback
+{
+public:
+	LLWearInventoryCategoryCallback(const LLUUID& cat_id, bool append)
+	{
+		mCatID = cat_id;
+		mAppend = append;
+	}
+	void fire(const LLUUID& item_id)
+	{
+		/*
+		 * Do nothing.  We only care about the destructor
+		 *
+		 * The reason for this is that this callback is used in a hack where the
+		 * same callback is given to dozens of items, and the destructor is called
+		 * after the last item has fired the event and dereferenced it -- if all
+		 * the events actually fire!
+		 */
+	}
+
+protected:
+	~LLWearInventoryCategoryCallback()
+	{
+		// Is the destructor called by ordinary dereference, or because the app's shutting down?
+		// If the inventory callback manager goes away, we're shutting down, no longer want the callback.
+		if( LLInventoryCallbackManager::is_instantiated() )
+		{
+			LLAppearanceManager::wearInventoryCategoryOnAvatar(gInventory.getCategory(mCatID), mAppend);
+		}
+		else
+		{
+			llwarns << "Dropping unhandled LLWearInventoryCategoryCallback" << llendl;
+		}
+	}
+
+private:
+	LLUUID mCatID;
+	bool mAppend;
+};
+
+class LLOutfitObserver : public LLInventoryFetchObserver
+{
+public:
+	LLOutfitObserver(const LLUUID& cat_id, bool copy_items, bool append) :
+		mCatID(cat_id),
+		mCopyItems(copy_items),
+		mAppend(append)
+	{}
+	~LLOutfitObserver() {}
+	virtual void done(); //public
+
+protected:
+	LLUUID mCatID;
+	bool mCopyItems;
+	bool mAppend;
+};
+
+void LLOutfitObserver::done()
+{
+	// We now have an outfit ready to be copied to agent inventory. Do
+	// it, and wear that outfit normally.
+	if(mCopyItems)
+	{
+		LLInventoryCategory* cat = gInventory.getCategory(mCatID);
+		std::string name;
+		if(!cat)
+		{
+			// should never happen.
+			name = "New Outfit";
+		}
+		else
+		{
+			name = cat->getName();
+		}
+		LLViewerInventoryItem* item = NULL;
+		item_ref_t::iterator it = mComplete.begin();
+		item_ref_t::iterator end = mComplete.end();
+		LLUUID pid;
+		for(; it < end; ++it)
+		{
+			item = (LLViewerInventoryItem*)gInventory.getItem(*it);
+			if(item)
+			{
+				if(LLInventoryType::IT_GESTURE == item->getInventoryType())
+				{
+					pid = gInventory.findCategoryUUIDForType(LLAssetType::AT_GESTURE);
+				}
+				else
+				{
+					pid = gInventory.findCategoryUUIDForType(LLAssetType::AT_CLOTHING);
+				}
+				break;
+			}
+		}
+		if(pid.isNull())
+		{
+			pid = gInventory.getRootFolderID();
+		}
+		
+		LLUUID cat_id = gInventory.createNewCategory(
+			pid,
+			LLAssetType::AT_NONE,
+			name);
+		mCatID = cat_id;
+		LLPointer<LLInventoryCallback> cb = new LLWearInventoryCategoryCallback(mCatID, mAppend);
+		it = mComplete.begin();
+		for(; it < end; ++it)
+		{
+			item = (LLViewerInventoryItem*)gInventory.getItem(*it);
+			if(item)
+			{
+				copy_inventory_item(
+					gAgent.getID(),
+					item->getPermissions().getOwner(),
+					item->getUUID(),
+					cat_id,
+					std::string(),
+					cb);
+			}
+		}
+	}
+	else
+	{
+		// Wear the inventory category.
+		LLAppearanceManager::wearInventoryCategoryOnAvatar(gInventory.getCategory(mCatID), mAppend);
+	}
+}
+
+class LLOutfitFetch : public LLInventoryFetchDescendentsObserver
+{
+public:
+	LLOutfitFetch(bool copy_items, bool append) : mCopyItems(copy_items), mAppend(append) {}
+	~LLOutfitFetch() {}
+	virtual void done();
+protected:
+	bool mCopyItems;
+	bool mAppend;
+};
+
+void LLOutfitFetch::done()
+{
+	// What we do here is get the complete information on the items in
+	// the library, and set up an observer that will wait for that to
+	// happen.
+	LLInventoryModel::cat_array_t cat_array;
+	LLInventoryModel::item_array_t item_array;
+	gInventory.collectDescendents(mCompleteFolders.front(),
+								  cat_array,
+								  item_array,
+								  LLInventoryModel::EXCLUDE_TRASH);
+	S32 count = item_array.count();
+	if(!count)
+	{
+		llwarns << "Nothing fetched in category " << mCompleteFolders.front()
+				<< llendl;
+		//dec_busy_count();
+		gInventory.removeObserver(this);
+		delete this;
+		return;
+	}
+
+	LLOutfitObserver* outfit_observer = new LLOutfitObserver(mCompleteFolders.front(), mCopyItems, mAppend);
+	LLInventoryFetchObserver::item_ref_t ids;
+	for(S32 i = 0; i < count; ++i)
+	{
+		ids.push_back(item_array.get(i)->getUUID());
+	}
+
+	// clean up, and remove this as an observer since the call to the
+	// outfit could notify observers and throw us into an infinite
+	// loop.
+	//dec_busy_count();
+	gInventory.removeObserver(this);
+	delete this;
+
+	// increment busy count and either tell the inventory to check &
+	// call done, or add this object to the inventory for observation.
+	//inc_busy_count();
+
+	// do the fetch
+	outfit_observer->fetchItems(ids);
+	if(outfit_observer->isEverythingComplete())
+	{
+		// everything is already here - call done.
+		outfit_observer->done();
+	}
+	else
+	{
+		// it's all on it's way - add an observer, and the inventory
+		// will call done for us when everything is here.
+		gInventory.addObserver(outfit_observer);
+	}
+}
+
+class LLUpdateAppearanceOnCount: public LLInventoryCallback
+{
+public:
+	LLUpdateAppearanceOnCount(S32 count):
+		mCount(count)
+	{
+	}
+
+	virtual ~LLUpdateAppearanceOnCount()
+	{
+	}
+
+	/* virtual */ void fire(const LLUUID& inv_item)
+	{
+		mCount--;
+		if (mCount==0)
+		{
+			done();
+		}
+	}
+
+	void done()
+	{
+		LLAppearanceManager::updateAppearanceFromCOF();
+	}
+private:
+	S32 mCount;
+};
+
+struct LLFoundData
+{
+	LLFoundData(const LLUUID& item_id,
+				const LLUUID& asset_id,
+				const std::string& name,
+				LLAssetType::EType asset_type) :
+		mItemID(item_id),
+		mAssetID(asset_id),
+		mName(name),
+		mAssetType(asset_type),
+		mWearable( NULL ) {}
+	
+	LLUUID mItemID;
+	LLUUID mAssetID;
+	std::string mName;
+	LLAssetType::EType mAssetType;
+	LLWearable* mWearable;
+};
+
+	
+struct LLWearableHoldingPattern
+{
+	LLWearableHoldingPattern() : mResolved(0) {}
+	~LLWearableHoldingPattern()
+	{
+		for_each(mFoundList.begin(), mFoundList.end(), DeletePointer());
+		mFoundList.clear();
+	}
+	typedef std::list<LLFoundData*> found_list_t;
+	found_list_t mFoundList;
+	S32 mResolved;
+	bool append;
+};
+
+
+void removeDuplicateItems(LLInventoryModel::item_array_t& dst, const LLInventoryModel::item_array_t& src)
+{
+	LLInventoryModel::item_array_t new_dst;
+	std::set<LLUUID> mark_inventory;
+	std::set<LLUUID> mark_asset;
+
+	S32 inventory_dups = 0;
+	S32 asset_dups = 0;
+	
+	for (LLInventoryModel::item_array_t::const_iterator src_pos = src.begin();
+		  src_pos != src.end();
+		  ++src_pos)
+	{
+		LLUUID src_item_id = (*src_pos)->getLinkedUUID();
+		mark_inventory.insert(src_item_id);
+		LLUUID src_asset_id = (*src_pos)->getAssetUUID();
+		mark_asset.insert(src_asset_id);
+	}
+
+	for (LLInventoryModel::item_array_t::const_iterator dst_pos = dst.begin();
+		  dst_pos != dst.end();
+		  ++dst_pos)
+	{
+		LLUUID dst_item_id = (*dst_pos)->getLinkedUUID();
+
+		if (mark_inventory.find(dst_item_id) == mark_inventory.end())
+		{
+		}
+		else
+		{
+			inventory_dups++;
+		}
+
+		LLUUID dst_asset_id = (*dst_pos)->getAssetUUID();
+
+		if (mark_asset.find(dst_asset_id) == mark_asset.end())
+		{
+			// Item is not already present in COF.
+			new_dst.put(*dst_pos);
+			mark_asset.insert(dst_item_id);
+		}
+		else
+		{
+			asset_dups++;
+		}
+	}
+	llinfos << "removeDups, original " << dst.count() << " final " << new_dst.count()
+			<< " inventory dups " << inventory_dups << " asset_dups " << asset_dups << llendl;
+	
+	dst = new_dst;
+}
+
+
+/* static */ LLUUID LLAppearanceManager::getCOF()
+{
+	return gInventory.findCategoryUUIDForType(LLAssetType::AT_CURRENT_OUTFIT);
+}
+
+// Update appearance from outfit folder.
+/* static */ void LLAppearanceManager::changeOutfit(bool proceed, const LLUUID& category, bool append, bool follow_folder_links)
+{
+	if (!proceed)
+		return;
+	
+	updateCOFFromOutfit(category, append, follow_folder_links);
+}
+
+// Update COF contents from outfit folder.
+/* static */ void LLAppearanceManager::updateCOFFromOutfit(const LLUUID& category, bool append, bool follow_folder_links)
+{
+	// BAP consolidate into one "get all 3 types of descendents" function, use both places.
+	LLInventoryModel::item_array_t wear_items;
+	LLInventoryModel::item_array_t	obj_items;
+	LLInventoryModel::item_array_t	gest_items;
+	getUserDescendents(category, wear_items, obj_items, gest_items, follow_folder_links);
+
+	// Find all the wearables that are in the category's subtree.	
+	lldebugs << "updateCOFFromOutfit()" << llendl;
+	if( !wear_items.count() && !obj_items.count() && !gest_items.count())
+	{
+		LLNotifications::instance().add("CouldNotPutOnOutfit");
+		return;
+	}
+		
+	const LLUUID &current_outfit_id = gInventory.findCategoryUUIDForType(LLAssetType::AT_CURRENT_OUTFIT);
+	// Processes that take time should show the busy cursor
+	//inc_busy_count();
+		
+	LLInventoryModel::cat_array_t cof_cats;
+	LLInventoryModel::item_array_t cof_items;
+	gInventory.collectDescendents(current_outfit_id, cof_cats, cof_items,
+								  LLInventoryModel::EXCLUDE_TRASH);
+	if (append)
+	{
+		// Remove duplicates
+		removeDuplicateItems(wear_items, cof_items);
+		removeDuplicateItems(obj_items, cof_items);
+		removeDuplicateItems(gest_items, cof_items);
+	}
+			
+
+	if (wear_items.count() > 0 || obj_items.count() > 0)
+	{
+		if (!append) 
+		{
+			// Remove all current outfit folder links if we're now replacing the contents.
+			for (S32 i = 0; i < cof_items.count(); ++i)
+			{
+				gInventory.purgeObject(cof_items.get(i)->getUUID());
+			}
+		}
+	}
+
+	// BAP should we just link all contents, rather than restricting to these 3 types?
+
+	S32 total_links = gest_items.count() + wear_items.count() + obj_items.count();
+	LLPointer<LLUpdateAppearanceOnCount> link_waiter = new LLUpdateAppearanceOnCount(total_links);
+	
+	// Link all gestures in this folder
+	if (gest_items.count() > 0)
+	{
+		llinfos << "Linking " << gest_items.count() << " gestures" << llendl;
+		for (S32 i = 0; i < gest_items.count(); ++i)
+		{
+			const LLInventoryItem* gest_item = gest_items.get(i).get();
+			link_inventory_item(gAgent.getID(), gest_item->getLinkedUUID(), current_outfit_id,
+								gest_item->getName(),
+								LLAssetType::AT_LINK, link_waiter);
+		}
+	}
+
+	// Link all wearables
+	if(wear_items.count() > 0)
+	{
+		llinfos << "Linking " << wear_items.count() << " wearables" << llendl;
+		for(S32 i = 0; i < wear_items.count(); ++i)
+		{
+			// Populate the current outfit folder with links to the newly added wearables
+			const LLInventoryItem* wear_item = wear_items.get(i).get();
+			link_inventory_item(gAgent.getID(), 
+								wear_item->getLinkedUUID(), // If this item is a link, then we'll use the linked item's UUID.
+								current_outfit_id, 
+								wear_item->getName(),
+								LLAssetType::AT_LINK, 
+								link_waiter);
+		}
+	}
+
+	// Link all attachments.
+	if( obj_items.count() > 0 )
+	{
+		llinfos << "Linking " << obj_items.count() << " attachments" << llendl;
+		LLVOAvatar* avatar = gAgent.getAvatarObject();
+		if( avatar )
+		{
+			for(S32 i = 0; i < obj_items.count(); ++i)
+			{
+				const LLInventoryItem* obj_item = obj_items.get(i).get();
+				link_inventory_item(gAgent.getID(), 
+									obj_item->getLinkedUUID(), // If this item is a link, then we'll use the linked item's UUID.
+									current_outfit_id, 
+									obj_item->getName(),
+									LLAssetType::AT_LINK, link_waiter);
+			}
+		}
+	}
+
+	// In the particular case that we're switching to a different outfit,
+	// create a link to the folder that we wore.
+	LLViewerInventoryCategory* catp = gInventory.getCategory(category);
+	if (!append && catp && catp->getPreferredType() == LLAssetType::AT_OUTFIT)
+	{
+		link_inventory_item(gAgent.getID(), category, current_outfit_id, catp->getName(),
+							LLAssetType::AT_LINK_FOLDER, LLPointer<LLInventoryCallback>(NULL));
+	}
+}
+
+/* static */
+void LLAppearanceManager::onWearableAssetFetch(LLWearable* wearable, void* data)
+{
+	LLWearableHoldingPattern* holder = (LLWearableHoldingPattern*)data;
+	bool append = holder->append;
+	
+	if(wearable)
+	{
+		for (LLWearableHoldingPattern::found_list_t::iterator iter = holder->mFoundList.begin();
+			 iter != holder->mFoundList.end(); ++iter)
+		{
+			LLFoundData* data = *iter;
+			if(wearable->getAssetID() == data->mAssetID)
+			{
+				data->mWearable = wearable;
+				break;
+			}
+		}
+	}
+	holder->mResolved += 1;
+	if(holder->mResolved >= (S32)holder->mFoundList.size())
+	{
+		LLAppearanceManager::updateAgentWearables(holder, append);
+	}
+}
+
+/* static */
+void LLAppearanceManager::updateAgentWearables(LLWearableHoldingPattern* holder, bool append)
+{
+	lldebugs << "updateAgentWearables()" << llendl;
+	LLInventoryItem::item_array_t items;
+	LLDynamicArray< LLWearable* > wearables;
+
+	// For each wearable type, find the first instance in the category
+	// that we recursed through.
+	for( S32 i = 0; i < WT_COUNT; i++ )
+	{
+		for (LLWearableHoldingPattern::found_list_t::iterator iter = holder->mFoundList.begin();
+			 iter != holder->mFoundList.end(); ++iter)
+		{
+			LLFoundData* data = *iter;
+			LLWearable* wearable = data->mWearable;
+			if( wearable && ((S32)wearable->getType() == i) )
+			{
+				LLViewerInventoryItem* item;
+				item = (LLViewerInventoryItem*)gInventory.getItem(data->mItemID);
+				if( item && (item->getAssetUUID() == wearable->getAssetID()) )
+				{
+					items.put(item);
+					wearables.put(wearable);
+				}
+				break;
+			}
+		}
+	}
+
+	if(wearables.count() > 0)
+	{
+		gAgentWearables.setWearableOutfit(items, wearables, !append);
+		gInventory.notifyObservers();
+	}
+
+	delete holder;
+
+//	dec_busy_count();
+}
+
+/* static */ void LLAppearanceManager::updateAppearanceFromCOF()
+{
+	bool follow_folder_links = true;
+	LLUUID current_outfit_id = getCOF();
+
+	// Find all the wearables that are in the COF's subtree.	
+	lldebugs << "LLAppearanceManager::updateFromCOF()" << llendl;
+	LLInventoryModel::item_array_t wear_items;
+	LLInventoryModel::item_array_t obj_items;
+	LLInventoryModel::item_array_t gest_items;
+	getUserDescendents(current_outfit_id, wear_items, obj_items, gest_items, follow_folder_links);
+	
+	if( !wear_items.count() && !obj_items.count() && !gest_items.count())
+	{
+		LLNotifications::instance().add("CouldNotPutOnOutfit");
+		return;
+	}
+		
+	// Processes that take time should show the busy cursor
+	//inc_busy_count(); // BAP this is currently a no-op in llinventorybridge.cpp - do we need it?
+		
+	// Activate all gestures in this folder
+	if (gest_items.count() > 0)
+	{
+		llinfos << "Activating " << gest_items.count() << " gestures" << llendl;
+
+		LLGestureManager::instance().activateGestures(gest_items);
+
+		// Update the inventory item labels to reflect the fact
+		// they are active.
+		LLViewerInventoryCategory* catp = gInventory.getCategory(current_outfit_id);
+		if (catp)
+		{
+			gInventory.updateCategory(catp);
+			gInventory.notifyObservers();
+		}
+	}
+
+	if(wear_items.count() > 0)
+	{
+		// Note: can't do normal iteration, because if all the
+		// wearables can be resolved immediately, then the
+		// callback will be called (and this object deleted)
+		// before the final getNextData().
+		LLWearableHoldingPattern* holder = new LLWearableHoldingPattern;
+		LLFoundData* found;
+		LLDynamicArray<LLFoundData*> found_container;
+		for(S32 i = 0; i  < wear_items.count(); ++i)
+		{
+			found = new LLFoundData(wear_items.get(i)->getUUID(),
+									wear_items.get(i)->getAssetUUID(),
+									wear_items.get(i)->getName(),
+									wear_items.get(i)->getType());
+			holder->mFoundList.push_front(found);
+			found_container.put(found);
+		}
+		for(S32 i = 0; i < wear_items.count(); ++i)
+		{
+			holder->append = false;
+			found = found_container.get(i);
+				
+			// Fetch the wearables about to be worn.
+			LLWearableList::instance().getAsset(found->mAssetID,
+												found->mName,
+												found->mAssetType,
+												LLAppearanceManager::onWearableAssetFetch,
+												(void*)holder);
+		}
+	}
+
+
+	//If the folder doesn't contain only gestures, take off all attachments.
+	if (!(wear_items.count() == 0 && obj_items.count() == 0 && gest_items.count() > 0) )
+	{
+		LLAgentWearables::userRemoveAllAttachments(NULL);
+	}
+
+	if( obj_items.count() > 0 )
+	{
+		// We've found some attachments.  Add these.
+		LLVOAvatar* avatar = gAgent.getAvatarObject();
+		if( avatar )
+		{
+			LLAgentWearables::userAttachMultipleAttachments(obj_items);
+		}
+	}
+}
+
+/* static */ void LLAppearanceManager::getUserDescendents(const LLUUID& category, 
+														  LLInventoryModel::item_array_t& wear_items,
+														  LLInventoryModel::item_array_t& obj_items,
+														  LLInventoryModel::item_array_t& gest_items,
+														  bool follow_folder_links)
+{
+	LLInventoryModel::cat_array_t wear_cats;
+	LLFindWearables is_wearable;
+	gInventory.collectDescendentsIf(category,
+									wear_cats,
+									wear_items,
+									LLInventoryModel::EXCLUDE_TRASH,
+									is_wearable,
+									follow_folder_links);
+
+	LLInventoryModel::cat_array_t obj_cats;
+	LLIsType is_object( LLAssetType::AT_OBJECT );
+	gInventory.collectDescendentsIf(category,
+									obj_cats,
+									obj_items,
+									LLInventoryModel::EXCLUDE_TRASH,
+									is_object,
+									follow_folder_links);
+
+	// Find all gestures in this folder
+	LLInventoryModel::cat_array_t gest_cats;
+	LLIsType is_gesture( LLAssetType::AT_GESTURE );
+	gInventory.collectDescendentsIf(category,
+									gest_cats,
+									gest_items,
+									LLInventoryModel::EXCLUDE_TRASH,
+									is_gesture,
+									follow_folder_links);
+}
+
+void LLAppearanceManager::wearInventoryCategory(LLInventoryCategory* category, bool copy, bool append)
+{
+	if(!category) return;
+
+	lldebugs << "wearInventoryCategory( " << category->getName()
+			 << " )" << llendl;
+	// What we do here is get the complete information on the items in
+	// the inventory, and set up an observer that will wait for that to
+	// happen.
+	LLOutfitFetch* outfit_fetcher = new LLOutfitFetch(copy, append);
+	LLInventoryFetchDescendentsObserver::folder_ref_t folders;
+	folders.push_back(category->getUUID());
+	outfit_fetcher->fetchDescendents(folders);
+	//inc_busy_count();
+	if(outfit_fetcher->isEverythingComplete())
+	{
+		// everything is already here - call done.
+		outfit_fetcher->done();
+	}
+	else
+	{
+		// it's all on it's way - add an observer, and the inventory
+		// will call done for us when everything is here.
+		gInventory.addObserver(outfit_fetcher);
+	}
+}
+
+// *NOTE: hack to get from avatar inventory to avatar
+/* static */
+void LLAppearanceManager::wearInventoryCategoryOnAvatar( LLInventoryCategory* category, bool append )
+{
+	// Avoid unintentionally overwriting old wearables.  We have to do
+	// this up front to avoid having to deal with the case of multiple
+	// wearables being dirty.
+	if(!category) return;
+	lldebugs << "wearInventoryCategoryOnAvatar( " << category->getName()
+			 << " )" << llendl;
+			 	
+	bool follow_folder_links = (category->getPreferredType() == LLAssetType::AT_CURRENT_OUTFIT || category->getPreferredType() == LLAssetType::AT_OUTFIT ); 
+	if( gFloaterCustomize )
+	{
+		gFloaterCustomize->askToSaveIfDirty(boost::bind(LLAppearanceManager::changeOutfit, _1, category->getUUID(), append, follow_folder_links));
+	}
+	else
+	{
+		LLAppearanceManager::changeOutfit(TRUE, category->getUUID(), append, follow_folder_links );
+	}
+}
+
+/* static */
+void LLAppearanceManager::wearOutfitByName(const std::string& name)
+{
+	llinfos << "Wearing category " << name << llendl;
+	//inc_busy_count();
+
+	LLInventoryModel::cat_array_t cat_array;
+	LLInventoryModel::item_array_t item_array;
+	LLNameCategoryCollector has_name(name);
+	gInventory.collectDescendentsIf(gInventory.getRootFolderID(),
+									cat_array,
+									item_array,
+									LLInventoryModel::EXCLUDE_TRASH,
+									has_name);
+	bool copy_items = false;
+	LLInventoryCategory* cat = NULL;
+	if (cat_array.count() > 0)
+	{
+		// Just wear the first one that matches
+		cat = cat_array.get(0);
+	}
+	else
+	{
+		gInventory.collectDescendentsIf(LLUUID::null,
+										cat_array,
+										item_array,
+										LLInventoryModel::EXCLUDE_TRASH,
+										has_name);
+		if(cat_array.count() > 0)
+		{
+			cat = cat_array.get(0);
+			copy_items = true;
+		}
+	}
+
+	if(cat)
+	{
+		LLAppearanceManager::wearInventoryCategory(cat, copy_items, false);
+	}
+	else
+	{
+		llwarns << "Couldn't find outfit " <<name<< " in wearOutfitByName()"
+				<< llendl;
+	}
+
+	//dec_busy_count();
+}
+
+void LLAppearanceManager::wearItem( LLInventoryItem* item, bool do_update )
+{
+	// BAP add check for already in COF.
+	LLPointer<LLInventoryCallback> cb = do_update ? new ModifiedCOFCallback : 0;
+	link_inventory_item( gAgent.getID(),
+						 item->getLinkedUUID(),
+						 getCOF(),
+						 item->getName(),
+						 LLAssetType::AT_LINK,
+						 cb);
+}
+
+void LLAppearanceManager::wearEnsemble( LLInventoryCategory* cat, bool do_update )
+{
+	// BAP add check for already in COF.
+	LLPointer<LLInventoryCallback> cb = do_update ? new ModifiedCOFCallback : 0;
+	link_inventory_item( gAgent.getID(),
+						 cat->getLinkedUUID(),
+						 getCOF(),
+						 cat->getName(),
+						 LLAssetType::AT_LINK_FOLDER,
+						 cb);
+}
+
diff --git a/indra/newview/llappearancemgr.h b/indra/newview/llappearancemgr.h
new file mode 100644
index 00000000000..89b95833d7b
--- /dev/null
+++ b/indra/newview/llappearancemgr.h
@@ -0,0 +1,70 @@
+/** 
+ * @file llappearancemgr.h
+ * @brief Manager for initiating appearance changes on the viewer
+ *
+ * $LicenseInfo:firstyear=2004&license=viewergpl$
+ * 
+ * Copyright (c) 2004-2009, Linden Research, Inc.
+ * 
+ * Second Life Viewer Source Code
+ * The source code in this file ("Source Code") is provided by Linden Lab
+ * to you under the terms of the GNU General Public License, version 2.0
+ * ("GPL"), unless you have obtained a separate licensing agreement
+ * ("Other License"), formally executed by you and Linden Lab.  Terms of
+ * the GPL can be found in doc/GPL-license.txt in this distribution, or
+ * online at http://secondlifegrid.net/programs/open_source/licensing/gplv2
+ * 
+ * There are special exceptions to the terms and conditions of the GPL as
+ * it is applied to this Source Code. View the full text of the exception
+ * in the file doc/FLOSS-exception.txt in this software distribution, or
+ * online at
+ * http://secondlifegrid.net/programs/open_source/licensing/flossexception
+ * 
+ * By copying, modifying or distributing this software, you acknowledge
+ * that you have read and understood your obligations described above,
+ * and agree to abide by those obligations.
+ * 
+ * ALL LINDEN LAB SOURCE CODE IS PROVIDED "AS IS." LINDEN LAB MAKES NO
+ * WARRANTIES, EXPRESS, IMPLIED OR OTHERWISE, REGARDING ITS ACCURACY,
+ * COMPLETENESS OR PERFORMANCE.
+ * $/LicenseInfo$
+ */
+
+#ifndef LL_LLAPPEARANCEMGR_H
+#define LL_LLAPPEARANCEMGR_H
+
+#include "llsingleton.h"
+#include "llinventorymodel.h"
+
+class LLWearable;
+struct LLWearableHoldingPattern;
+
+class LLAppearanceManager: public LLSingleton<LLAppearanceManager>
+{
+public:
+	static void updateAppearanceFromCOF();
+	static bool needToSaveCOF();
+	static void changeOutfit(bool proceed, const LLUUID& category, bool append, bool follow_folder_links);
+	static void updateCOFFromOutfit(const LLUUID& category, bool append, bool follow_folder_links);
+	static void wearInventoryCategory(LLInventoryCategory* category, bool copy, bool append);
+	static void wearInventoryCategoryOnAvatar(LLInventoryCategory* category, bool append);
+	static void wearOutfitByName(const std::string& name);
+
+	// Add COF link to individual item.
+	static void wearItem(LLInventoryItem* item, bool do_update = true);
+
+	// Add COF link to ensemble folder.
+	static void wearEnsemble(LLInventoryCategory* item, bool do_update = true);
+
+private:
+	static LLUUID getCOF();
+	static void getUserDescendents(const LLUUID& category, 
+								   LLInventoryModel::item_array_t& wear_items,
+								   LLInventoryModel::item_array_t& obj_items,
+								   LLInventoryModel::item_array_t& gest_items,
+								   bool follow_folder_links);
+	static void onWearableAssetFetch(LLWearable* wearable, void* data);
+	static void updateAgentWearables(LLWearableHoldingPattern* holder, bool append);
+};
+
+#endif
diff --git a/indra/newview/llfolderviewitem.cpp b/indra/newview/llfolderviewitem.cpp
index f1e499e14b9..490929e5a66 100644
--- a/indra/newview/llfolderviewitem.cpp
+++ b/indra/newview/llfolderviewitem.cpp
@@ -1183,6 +1183,8 @@ void LLFolderViewFolder::filter( LLInventoryFilter& filter)
 	// you will automatically fail this time, so we only
 	// check against items that have passed the filter
 	S32 must_pass_generation = filter.getMustPassGeneration();
+	
+	bool autoopen_folders = (filter.hasFilterString());
 
 	// if we have already been filtered against this generation, skip out
 	if (getCompletedFilterGeneration() >= filter_generation)
@@ -1255,7 +1257,7 @@ void LLFolderViewFolder::filter( LLInventoryFilter& filter)
 			if ((*fit)->getFiltered() || (*fit)->hasFilteredDescendants(filter.getMinRequiredGeneration()))
 			{
 				mMostFilteredDescendantGeneration = filter_generation;
-				if (getRoot()->needsAutoSelect())
+				if (getRoot()->needsAutoSelect() && autoopen_folders)
 				{
 					(*fit)->setOpenArrangeRecursively(TRUE);
 				}
@@ -1271,7 +1273,7 @@ void LLFolderViewFolder::filter( LLInventoryFilter& filter)
 		if ((*fit)->getFiltered() || (*fit)->hasFilteredDescendants(filter_generation))
 		{
 			mMostFilteredDescendantGeneration = filter_generation;
-			if (getRoot()->needsAutoSelect())
+			if (getRoot()->needsAutoSelect() && autoopen_folders)
 			{
 				(*fit)->setOpenArrangeRecursively(TRUE);
 			}
diff --git a/indra/newview/llinventorybridge.cpp b/indra/newview/llinventorybridge.cpp
index adc73111e01..eb070784022 100644
--- a/indra/newview/llinventorybridge.cpp
+++ b/indra/newview/llinventorybridge.cpp
@@ -92,6 +92,7 @@
 #include "llsidetray.h"
 #include "llfloateropenobject.h"
 #include "lltrans.h"
+#include "llappearancemgr.h"
 
 using namespace LLOldEvents;
 
@@ -111,12 +112,7 @@ void dec_busy_count()
 }
 
 // Function declarations
-struct LLWearableHoldingPattern;
 void wear_add_inventory_item_on_avatar(LLInventoryItem* item);
-void wear_inventory_category_on_avatar(LLInventoryCategory* category, BOOL append);
-void wear_inventory_category_on_avatar_step2( BOOL proceed, LLUUID category, BOOL append, BOOL follow_folder_links);
-void wear_inventory_category_on_avatar_loop(LLWearable* wearable, void*);
-void wear_inventory_category_on_avatar_step3(LLWearableHoldingPattern* holder, BOOL append);
 void remove_inventory_category_from_avatar(LLInventoryCategory* category);
 void remove_inventory_category_from_avatar_step2( BOOL proceed, LLUUID category_id);
 bool move_task_inventory_callback(const LLSD& notification, const LLSD& response, LLMoveInv*);
@@ -161,8 +157,6 @@ std::string ICON_NAME[ICON_NAME_COUNT] =
 	"inv_item_linkfolder.tga"
 };
 
-BOOL gAddToOutfit = FALSE;
-
 
 // +=================================================+
 // |        LLInventoryPanelObserver                 |
@@ -474,7 +468,7 @@ BOOL LLInvFVBridge::isClipboardPasteableAsLink() const
 	{
 		return FALSE;
 	}
-	LLInventoryModel* model = getInventoryModel();
+	const LLInventoryModel* model = getInventoryModel();
 	if (!model)
 	{
 		return FALSE;
@@ -485,7 +479,7 @@ BOOL LLInvFVBridge::isClipboardPasteableAsLink() const
 	S32 count = objects.count();
 	for(S32 i = 0; i < count; i++)
 	{
-		LLInventoryItem *item = model->getItem(objects.get(i));
+		const LLInventoryItem *item = model->getItem(objects.get(i));
 		if (item)
 		{
 			if (!LLAssetType::lookupCanLink(item->getActualType()))
@@ -665,7 +659,7 @@ BOOL LLInvFVBridge::isInTrash() const
 {
 	LLInventoryModel* model = getInventoryModel();
 	if(!model) return FALSE;
-	const LLUUID& trash_id = model->findCategoryUUIDForType(LLAssetType::AT_TRASH);
+	const LLUUID trash_id = model->findCategoryUUIDForType(LLAssetType::AT_TRASH);
 	return model->isObjectDescendentOf(mUUID, trash_id);
 }
 
@@ -673,12 +667,12 @@ BOOL LLInvFVBridge::isLinkedObjectInTrash() const
 {
 	if (isInTrash()) return TRUE;
 
-	LLInventoryObject *obj = getInventoryObject();
+	const LLInventoryObject *obj = getInventoryObject();
 	if (obj && obj->getIsLinkType())
 	{
 		LLInventoryModel* model = getInventoryModel();
 		if(!model) return FALSE;
-		const LLUUID& trash_id = model->findCategoryUUIDForType(LLAssetType::AT_TRASH);
+		const LLUUID trash_id = model->findCategoryUUIDForType(LLAssetType::AT_TRASH);
 		return model->isObjectDescendentOf(obj->getLinkedUUID(), trash_id);
 	}
 	return FALSE;
@@ -686,12 +680,24 @@ BOOL LLInvFVBridge::isLinkedObjectInTrash() const
 
 BOOL LLInvFVBridge::isAgentInventory() const
 {
-	LLInventoryModel* model = getInventoryModel();
+	const LLInventoryModel* model = getInventoryModel();
 	if(!model) return FALSE;
 	if(gInventory.getRootFolderID() == mUUID) return TRUE;
 	return model->isObjectDescendentOf(mUUID, gInventory.getRootFolderID());
 }
 
+BOOL LLInvFVBridge::isCOFFolder() const
+{
+	const LLInventoryModel* model = getInventoryModel();
+	if(!model) return TRUE;
+	const LLUUID cof_id = gInventory.findCategoryUUIDForType(LLAssetType::AT_CURRENT_OUTFIT);
+	if (mUUID == cof_id || model->isObjectDescendentOf(mUUID, cof_id))
+	{
+		return TRUE;
+	}
+	return FALSE;
+}
+
 BOOL LLInvFVBridge::isItemPermissive() const
 {
 	return FALSE;
@@ -993,7 +999,7 @@ void LLItemBridge::restoreItem()
 	if(item)
 	{
 		LLInventoryModel* model = getInventoryModel();
-		LLUUID new_parent = model->findCategoryUUIDForType(item->getType());
+		const LLUUID new_parent = model->findCategoryUUIDForType(item->getType());
 		// do not restamp on restore.
 		LLInvFVBridge::changeItemParent(model, item, new_parent, FALSE);
 	}
@@ -1026,8 +1032,7 @@ void LLItemBridge::restoreToWorld()
 	}
 
 	// Check if it's in the trash. (again similar to the normal rez logic)
-	LLUUID trash_id;
-	trash_id = gInventory.findCategoryUUIDForType(LLAssetType::AT_TRASH);
+	const LLUUID trash_id = gInventory.findCategoryUUIDForType(LLAssetType::AT_TRASH);
 	if(gInventory.isObjectDescendentOf(itemp->getUUID(), trash_id))
 	{
 		remove_from_inventory = TRUE;
@@ -1423,6 +1428,46 @@ BOOL LLFolderBridge::copyToClipboard() const
 	return FALSE;
 }
 
+BOOL LLFolderBridge::isClipboardPasteableAsLink() const
+{
+	// Check normal paste-as-link permissions
+	if (!LLInvFVBridge::isClipboardPasteableAsLink())
+	{
+		return FALSE;
+	}
+	
+	const LLInventoryModel* model = getInventoryModel();
+	if (!model)
+	{
+		return FALSE;
+	}
+
+	const LLViewerInventoryCategory *current_cat = getCategory();
+	if (current_cat)
+	{
+		const LLUUID &current_cat_id = current_cat->getUUID();
+		LLDynamicArray<LLUUID> objects;
+		LLInventoryClipboard::instance().retrieve(objects);
+		S32 count = objects.count();
+		for(S32 i = 0; i < count; i++)
+		{
+			const LLInventoryCategory *cat = model->getCategory(objects.get(i));
+			if (cat)
+			{
+				const LLUUID &cat_id = cat->getUUID();
+				// Don't allow recursive pasting
+				if ((cat_id == current_cat_id) || 
+					model->isObjectDescendentOf(current_cat_id, cat_id))
+				{
+					return FALSE;
+				}
+			}
+		}
+	}
+	return TRUE;
+
+}
+
 BOOL LLFolderBridge::dragCategoryIntoFolder(LLInventoryCategory* inv_cat,
 											BOOL drop)
 {
@@ -1436,8 +1481,8 @@ BOOL LLFolderBridge::dragCategoryIntoFolder(LLInventoryCategory* inv_cat,
 	LLVOAvatarSelf* avatar = gAgent.getAvatarObject();
 	if(!avatar) return FALSE;
 
-	// cannot drag into library
-	if(!isAgentInventory())
+	// cannot drag categories into library or COF
+	if(!isAgentInventory() || isCOFFolder())
 	{
 		return FALSE;
 	}
@@ -1456,14 +1501,14 @@ BOOL LLFolderBridge::dragCategoryIntoFolder(LLInventoryCategory* inv_cat,
 		const LLUUID& cat_id = inv_cat->getUUID();
 
 		// Is the destination the trash?
-		LLUUID trash_id;
-		trash_id = model->findCategoryUUIDForType(LLAssetType::AT_TRASH);
+		const LLUUID trash_id = model->findCategoryUUIDForType(LLAssetType::AT_TRASH);
 		BOOL move_is_into_trash = (mUUID == trash_id)
 				|| model->isObjectDescendentOf(mUUID, trash_id);
 		BOOL is_movable = (!LLAssetType::lookupIsProtectedCategoryType(inv_cat->getPreferredType()));
 		LLUUID current_outfit_id = model->findCategoryUUIDForType(LLAssetType::AT_CURRENT_OUTFIT);
 		BOOL move_is_into_current_outfit = (mUUID == current_outfit_id);
-		if (move_is_into_current_outfit)
+		BOOL move_is_into_outfit = (getCategory() && getCategory()->getPreferredType()==LLAssetType::AT_OUTFIT);
+		if (move_is_into_current_outfit || move_is_into_outfit)
 		{
 			// BAP - restrictions?
 			is_movable = true;
@@ -1533,16 +1578,25 @@ BOOL LLFolderBridge::dragCategoryIntoFolder(LLInventoryCategory* inv_cat,
 					}
 				}
 			}
-
-			if (current_outfit_id == mUUID) // if target is current outfit folder we use link
+			// if target is an outfit or current outfit folder we use link
+			if (move_is_into_current_outfit || move_is_into_outfit) 
 			{
-				link_inventory_item(
-					gAgent.getID(),
-					inv_cat->getUUID(),
-					mUUID,
-					inv_cat->getName(),
-					LLAssetType::AT_LINK_FOLDER,
-					LLPointer<LLInventoryCallback>(NULL));
+				// BAP - should skip if dup.
+				if (move_is_into_current_outfit)
+				{
+					LLAppearanceManager::wearEnsemble(inv_cat);
+				}
+				else
+				{
+					LLPointer<LLInventoryCallback> cb = NULL;
+					link_inventory_item(
+						gAgent.getID(),
+						inv_cat->getUUID(),
+						mUUID,
+						inv_cat->getName(),
+						LLAssetType::AT_LINK_FOLDER,
+						cb);
+				}
 			}
 			else
 			{
@@ -1856,7 +1910,7 @@ void LLInventoryCopyAndWearObserver::changed(U32 mask)
 				    mContentsCount)
 				{
 					gInventory.removeObserver(this);
-					wear_inventory_category(category, FALSE, TRUE);
+					LLAppearanceManager::wearInventoryCategory(category, FALSE, TRUE);
 					delete this;
 				}
 			}		
@@ -2117,8 +2171,7 @@ BOOL LLFolderBridge::removeItem()
 	LLInventoryModel* model = getInventoryModel();
 	if(!model) return FALSE;
 
-	LLUUID trash_id;
-	trash_id = model->findCategoryUUIDForType(LLAssetType::AT_TRASH);
+	LLUUID trash_id = model->findCategoryUUIDForType(LLAssetType::AT_TRASH);
 
 	// Look for any gestures and deactivate them
 	LLInventoryModel::cat_array_t	descendent_categories;
@@ -2176,7 +2229,7 @@ void LLFolderBridge::pasteFromClipboard()
 
 void LLFolderBridge::pasteLinkFromClipboard()
 {
-	LLInventoryModel* model = getInventoryModel();
+	const LLInventoryModel* model = getInventoryModel();
 	if(model)
 	{
 		LLDynamicArray<LLUUID> objects;
@@ -2224,7 +2277,7 @@ void LLFolderBridge::folderOptionsMenu()
 	if(!model) return;
 
 	const LLInventoryCategory* category = model->getCategory(mUUID);
-	bool is_default_folder = category &&
+	const bool is_default_folder = category &&
 		(LLAssetType::lookupIsProtectedCategoryType(category->getPreferredType()));
 	
 	// calling card related functionality for folders.
@@ -2262,10 +2315,6 @@ void LLFolderBridge::folderOptionsMenu()
 		}
 		mItems.push_back(std::string("Take Off Items"));
 	}
-	if (LLAssetType::AT_CURRENT_OUTFIT == category->getPreferredType())
-	{
-		mItems.push_back(std::string("Replace Outfit"));
-	}
 	hideContextEntries(*mMenu, mItems, disabled_items);
 }
 
@@ -2321,26 +2370,40 @@ void LLFolderBridge::buildContextMenu(LLMenuGL& menu, U32 flags)
 	{
 		LLViewerInventoryCategory *cat =  getCategory();
 
-		// Do not allow to create 2-level subfolder in the Calling Card/Friends folder. EXT-694.
-		if (!LLFriendCardsManager::instance().isCategoryInFriendFolder(cat))
-			mItems.push_back(std::string("New Folder"));
-		mItems.push_back(std::string("New Script"));
-		mItems.push_back(std::string("New Note"));
-		mItems.push_back(std::string("New Gesture"));
-		mItems.push_back(std::string("New Clothes"));
-		mItems.push_back(std::string("New Body Parts"));
-		mItems.push_back(std::string("Change Type"));
-
-		if (cat && LLAssetType::lookupIsProtectedCategoryType(cat->getPreferredType()))
+		if (!isCOFFolder() && cat &&
+			LLAssetType::lookupIsProtectedCategoryType(cat->getPreferredType()))
+		{
+			// Do not allow to create 2-level subfolder in the Calling Card/Friends folder. EXT-694.
+			if (!LLFriendCardsManager::instance().isCategoryInFriendFolder(cat))
+				mItems.push_back(std::string("New Folder"));
+			mItems.push_back(std::string("New Script"));
+			mItems.push_back(std::string("New Note"));
+			mItems.push_back(std::string("New Gesture"));
+			mItems.push_back(std::string("New Clothes"));
+			mItems.push_back(std::string("New Body Parts"));
+			mItems.push_back(std::string("Change Type"));
+			
+			LLViewerInventoryCategory *cat = getCategory();
+			if (cat && LLAssetType::lookupIsProtectedCategoryType(cat->getPreferredType()))
+			{
+				mDisabledItems.push_back(std::string("Change Type"));
+			}
+			
+			getClipboardEntries(false, mItems, mDisabledItems, flags);
+		}
+		else
 		{
-			mDisabledItems.push_back(std::string("Change Type"));
+			// Want some but not all of the items from getClipboardEntries for outfits.
+			if (cat && cat->getPreferredType()==LLAssetType::AT_OUTFIT)
+			{
+				mItems.push_back(std::string("Rename"));
+				mItems.push_back(std::string("Delete"));
+			}
 		}
-		
-		getClipboardEntries(false, mItems, mDisabledItems, flags);
 
 		//Added by spatters to force inventory pull on right-click to display folder options correctly. 07-17-06
 		mCallingCards = mWearables = FALSE;
-
+		
 		LLIsType is_callingcard(LLAssetType::AT_CALLINGCARD);
 		if (checkFolderForContentsOfType(model, is_callingcard))
 		{
@@ -2350,7 +2413,7 @@ void LLFolderBridge::buildContextMenu(LLMenuGL& menu, U32 flags)
 		LLFindWearables is_wearable;
 		LLIsType is_object( LLAssetType::AT_OBJECT );
 		LLIsType is_gesture( LLAssetType::AT_GESTURE );
-
+		
 		if (checkFolderForContentsOfType(model, is_wearable)  ||
 			checkFolderForContentsOfType(model, is_object) ||
 			checkFolderForContentsOfType(model, is_gesture) )
@@ -2364,7 +2427,10 @@ void LLFolderBridge::buildContextMenu(LLMenuGL& menu, U32 flags)
 
 		LLInventoryFetchDescendentsObserver::folder_ref_t folders;
 		LLViewerInventoryCategory* category = (LLViewerInventoryCategory*)model->getCategory(mUUID);
-		folders.push_back(category->getUUID());
+		if (category)
+		{
+			folders.push_back(category->getUUID());
+		}
 		fetch->fetchDescendents(folders);
 		inc_busy_count();
 		if(fetch->isEverythingComplete())
@@ -2570,7 +2636,7 @@ void LLFolderBridge::modifyOutfit(BOOL append)
 	
 	// BAP - was:
 	// wear_inventory_category_on_avatar( cat, append );
-	wear_inventory_category( cat, FALSE, append );
+	LLAppearanceManager::wearInventoryCategory( cat, FALSE, append );
 }
 
 // helper stuff
@@ -2650,6 +2716,10 @@ BOOL LLFolderBridge::dragItemIntoFolder(LLInventoryItem* inv_item,
 
 		LLUUID trash_id = model->findCategoryUUIDForType(LLAssetType::AT_TRASH);
 		BOOL move_is_into_trash = (mUUID == trash_id) || model->isObjectDescendentOf(mUUID, trash_id);
+		LLUUID current_outfit_id = model->findCategoryUUIDForType(LLAssetType::AT_CURRENT_OUTFIT);
+		BOOL move_is_into_current_outfit = (mUUID == current_outfit_id);
+		BOOL move_is_into_outfit = (getCategory() && getCategory()->getPreferredType()==LLAssetType::AT_OUTFIT);
+		
 		if(is_movable && move_is_into_trash)
 		{
 			switch(inv_item->getType())
@@ -2700,6 +2770,25 @@ BOOL LLFolderBridge::dragItemIntoFolder(LLInventoryItem* inv_item,
 					std::string(),
 					LLPointer<LLInventoryCallback>(NULL));
 			}
+			else if (move_is_into_current_outfit || move_is_into_outfit)
+			{
+				// BAP - should skip if dup.
+				if (move_is_into_current_outfit)
+				{
+					LLAppearanceManager::wearItem(inv_item);
+				}
+				else
+				{
+					LLPointer<LLInventoryCallback> cb = NULL;
+					link_inventory_item(
+						gAgent.getID(),
+						inv_item->getUUID(),
+						mUUID,
+						std::string(),
+						LLAssetType::AT_LINK,
+						cb);
+				}
+			}
 			else
 			{
 				// restamp if the move is into the trash.
@@ -3856,12 +3945,8 @@ void wear_inventory_item_on_avatar( LLInventoryItem* item )
 	{
 		lldebugs << "wear_inventory_item_on_avatar( " << item->getName()
 				 << " )" << llendl;
-			
-		LLWearableList::instance().getAsset(item->getAssetUUID(),
-							   item->getName(),
-							   item->getType(),
-							   LLWearableBridge::onWearOnAvatarArrived,
-							   new LLUUID(item->getUUID()));
+
+		LLAppearanceManager::wearItem(item);
 	}
 }
 
@@ -3880,667 +3965,6 @@ void wear_add_inventory_item_on_avatar( LLInventoryItem* item )
 	}
 }
 
-
-struct LLFoundData
-{
-	LLFoundData(const LLUUID& item_id,
-				const LLUUID& asset_id,
-				const std::string& name,
-				LLAssetType::EType asset_type) :
-		mItemID(item_id),
-		mAssetID(asset_id),
-		mName(name),
-		mAssetType(asset_type),
-		mWearable( NULL ) {}
-	
-	LLUUID mItemID;
-	LLUUID mAssetID;
-	std::string mName;
-	LLAssetType::EType mAssetType;
-	LLWearable* mWearable;
-};
-
-struct LLWearableHoldingPattern
-{
-	LLWearableHoldingPattern() : mResolved(0) {}
-	~LLWearableHoldingPattern()
-	{
-		for_each(mFoundList.begin(), mFoundList.end(), DeletePointer());
-		mFoundList.clear();
-	}
-	typedef std::list<LLFoundData*> found_list_t;
-	found_list_t mFoundList;
-	S32 mResolved;
-};
-
-
-class LLOutfitObserver : public LLInventoryFetchObserver
-{
-public:
-	LLOutfitObserver(const LLUUID& cat_id, bool copy_items, bool append) :
-		mCatID(cat_id),
-		mCopyItems(copy_items),
-		mAppend(append)
-	{}
-	~LLOutfitObserver() {}
-	virtual void done(); //public
-
-protected:
-	LLUUID mCatID;
-	bool mCopyItems;
-	bool mAppend;
-};
-
-class LLWearInventoryCategoryCallback : public LLInventoryCallback
-{
-public:
-	LLWearInventoryCategoryCallback(const LLUUID& cat_id, bool append)
-	{
-		mCatID = cat_id;
-		mAppend = append;
-	}
-	void fire(const LLUUID& item_id)
-	{
-		/*
-		 * Do nothing.  We only care about the destructor
-		 *
-		 * The reason for this is that this callback is used in a hack where the
-		 * same callback is given to dozens of items, and the destructor is called
-		 * after the last item has fired the event and dereferenced it -- if all
-		 * the events actually fire!
-		 */
-	}
-
-protected:
-	~LLWearInventoryCategoryCallback()
-	{
-		// Is the destructor called by ordinary dereference, or because the app's shutting down?
-		// If the inventory callback manager goes away, we're shutting down, no longer want the callback.
-		if( LLInventoryCallbackManager::is_instantiated() )
-		{
-			wear_inventory_category_on_avatar(gInventory.getCategory(mCatID), mAppend);
-		}
-		else
-		{
-			llwarns << "Dropping unhandled LLWearInventoryCategoryCallback" << llendl;
-		}
-	}
-
-private:
-	LLUUID mCatID;
-	bool mAppend;
-};
-
-void LLOutfitObserver::done()
-{
-	// We now have an outfit ready to be copied to agent inventory. Do
-	// it, and wear that outfit normally.
-	if(mCopyItems)
-	{
-		LLInventoryCategory* cat = gInventory.getCategory(mCatID);
-		std::string name;
-		if(!cat)
-		{
-			// should never happen.
-			name = "New Outfit";
-		}
-		else
-		{
-			name = cat->getName();
-		}
-		LLViewerInventoryItem* item = NULL;
-		item_ref_t::iterator it = mComplete.begin();
-		item_ref_t::iterator end = mComplete.end();
-		LLUUID pid;
-		for(; it < end; ++it)
-		{
-			item = (LLViewerInventoryItem*)gInventory.getItem(*it);
-			if(item)
-			{
-				if(LLInventoryType::IT_GESTURE == item->getInventoryType())
-				{
-					pid = gInventory.findCategoryUUIDForType(LLAssetType::AT_GESTURE);
-				}
-				else
-				{
-					pid = gInventory.findCategoryUUIDForType(LLAssetType::AT_CLOTHING);
-				}
-				break;
-			}
-		}
-		if(pid.isNull())
-		{
-			pid = gInventory.getRootFolderID();
-		}
-		
-		LLUUID cat_id = gInventory.createNewCategory(
-			pid,
-			LLAssetType::AT_NONE,
-			name);
-		mCatID = cat_id;
-		LLPointer<LLInventoryCallback> cb = new LLWearInventoryCategoryCallback(mCatID, mAppend);
-		it = mComplete.begin();
-		for(; it < end; ++it)
-		{
-			item = (LLViewerInventoryItem*)gInventory.getItem(*it);
-			if(item)
-			{
-				copy_inventory_item(
-					gAgent.getID(),
-					item->getPermissions().getOwner(),
-					item->getUUID(),
-					cat_id,
-					std::string(),
-					cb);
-			}
-		}
-	}
-	else
-	{
-		// Wear the inventory category.
-		wear_inventory_category_on_avatar(gInventory.getCategory(mCatID), mAppend);
-	}
-}
-
-class LLOutfitFetch : public LLInventoryFetchDescendentsObserver
-{
-public:
-	LLOutfitFetch(bool copy_items, bool append) : mCopyItems(copy_items), mAppend(append) {}
-	~LLOutfitFetch() {}
-	virtual void done();
-protected:
-	bool mCopyItems;
-	bool mAppend;
-};
-
-void LLOutfitFetch::done()
-{
-	// What we do here is get the complete information on the items in
-	// the library, and set up an observer that will wait for that to
-	// happen.
-	LLInventoryModel::cat_array_t cat_array;
-	LLInventoryModel::item_array_t item_array;
-	gInventory.collectDescendents(mCompleteFolders.front(),
-								  cat_array,
-								  item_array,
-								  LLInventoryModel::EXCLUDE_TRASH);
-	S32 count = item_array.count();
-	if(!count)
-	{
-		llwarns << "Nothing fetched in category " << mCompleteFolders.front()
-				<< llendl;
-		dec_busy_count();
-		gInventory.removeObserver(this);
-		delete this;
-		return;
-	}
-
-	LLOutfitObserver* outfit;
-	outfit = new LLOutfitObserver(mCompleteFolders.front(), mCopyItems, mAppend);
-	LLInventoryFetchObserver::item_ref_t ids;
-	for(S32 i = 0; i < count; ++i)
-	{
-		ids.push_back(item_array.get(i)->getUUID());
-	}
-
-	// clean up, and remove this as an observer since the call to the
-	// outfit could notify observers and throw us into an infinite
-	// loop.
-	dec_busy_count();
-	gInventory.removeObserver(this);
-	delete this;
-
-	// increment busy count and either tell the inventory to check &
-	// call done, or add this object to the inventory for observation.
-	inc_busy_count();
-
-	// do the fetch
-	outfit->fetchItems(ids);
-	if(outfit->isEverythingComplete())
-	{
-		// everything is already here - call done.
-		outfit->done();
-	}
-	else
-	{
-		// it's all on it's way - add an observer, and the inventory
-		// will call done for us when everything is here.
-		gInventory.addObserver(outfit);
-	}
-}
-
-void wear_outfit_by_name(const std::string& name)
-{
-	llinfos << "Wearing category " << name << llendl;
-	inc_busy_count();
-
-	LLInventoryModel::cat_array_t cat_array;
-	LLInventoryModel::item_array_t item_array;
-	LLNameCategoryCollector has_name(name);
-	gInventory.collectDescendentsIf(gInventory.getRootFolderID(),
-									cat_array,
-									item_array,
-									LLInventoryModel::EXCLUDE_TRASH,
-									has_name);
-	bool copy_items = false;
-	LLInventoryCategory* cat = NULL;
-	if (cat_array.count() > 0)
-	{
-		// Just wear the first one that matches
-		cat = cat_array.get(0);
-	}
-	else
-	{
-		gInventory.collectDescendentsIf(LLUUID::null,
-										cat_array,
-										item_array,
-										LLInventoryModel::EXCLUDE_TRASH,
-										has_name);
-		if(cat_array.count() > 0)
-		{
-			cat = cat_array.get(0);
-			copy_items = true;
-		}
-	}
-
-	if(cat)
-	{
-		wear_inventory_category(cat, copy_items, false);
-	}
-	else
-	{
-		llwarns << "Couldn't find outfit " <<name<< " in wear_outfit_by_name()"
-				<< llendl;
-	}
-
-	dec_busy_count();
-}
-
-void wear_inventory_category(LLInventoryCategory* category, bool copy, bool append)
-{
-	if(!category) return;
-
-	lldebugs << "wear_inventory_category( " << category->getName()
-			 << " )" << llendl;
-	// What we do here is get the complete information on the items in
-	// the inventory, and set up an observer that will wait for that to
-	// happen.
-	LLOutfitFetch* outfit;
-	outfit = new LLOutfitFetch(copy, append);
-	LLInventoryFetchDescendentsObserver::folder_ref_t folders;
-	folders.push_back(category->getUUID());
-	outfit->fetchDescendents(folders);
-	inc_busy_count();
-	if(outfit->isEverythingComplete())
-	{
-		// everything is already here - call done.
-		outfit->done();
-	}
-	else
-	{
-		// it's all on it's way - add an observer, and the inventory
-		// will call done for us when everything is here.
-		gInventory.addObserver(outfit);
-	}
-}
-
-// *NOTE: hack to get from avatar inventory to avatar
-void wear_inventory_category_on_avatar( LLInventoryCategory* category, BOOL append )
-{
-	// Avoid unintentionally overwriting old wearables.  We have to do
-	// this up front to avoid having to deal with the case of multiple
-	// wearables being dirty.
-	if(!category) return;
-	lldebugs << "wear_inventory_category_on_avatar( " << category->getName()
-			 << " )" << llendl;
-			 	
-	BOOL follow_folder_links = (category->getPreferredType() == LLAssetType::AT_CURRENT_OUTFIT || category->getPreferredType() == LLAssetType::AT_OUTFIT ); 
-	if( gFloaterCustomize )
-	{
-		gFloaterCustomize->askToSaveIfDirty(boost::bind(wear_inventory_category_on_avatar_step2, _1, category->getUUID(), append, follow_folder_links));
-	}
-	else
-	{
-		wear_inventory_category_on_avatar_step2(TRUE, category->getUUID(), append, follow_folder_links );
-	}
-}
-
-void removeDuplicateItems(LLInventoryModel::item_array_t& dst, const LLInventoryModel::item_array_t& src)
-{
-	LLInventoryModel::item_array_t new_dst;
-	std::set<LLUUID> mark_inventory;
-	std::set<LLUUID> mark_asset;
-
-	S32 inventory_dups = 0;
-	S32 asset_dups = 0;
-	
-	for (LLInventoryModel::item_array_t::const_iterator src_pos = src.begin();
-		  src_pos != src.end();
-		  ++src_pos)
-	{
-		LLUUID src_item_id = (*src_pos)->getLinkedUUID();
-		mark_inventory.insert(src_item_id);
-		LLUUID src_asset_id = (*src_pos)->getAssetUUID();
-		mark_asset.insert(src_asset_id);
-	}
-
-	for (LLInventoryModel::item_array_t::const_iterator dst_pos = dst.begin();
-		  dst_pos != dst.end();
-		  ++dst_pos)
-	{
-		LLUUID dst_item_id = (*dst_pos)->getLinkedUUID();
-
-		if (mark_inventory.find(dst_item_id) == mark_inventory.end())
-		{
-		}
-		else
-		{
-			inventory_dups++;
-		}
-
-		LLUUID dst_asset_id = (*dst_pos)->getAssetUUID();
-
-		if (mark_asset.find(dst_asset_id) == mark_asset.end())
-		{
-			// Item is not already present in COF.
-			new_dst.put(*dst_pos);
-			mark_asset.insert(dst_item_id);
-		}
-		else
-		{
-			asset_dups++;
-		}
-	}
-	llinfos << "removeDups, original " << dst.count() << " final " << new_dst.count()
-			<< " inventory dups " << inventory_dups << " asset_dups " << asset_dups << llendl;
-	
-	dst = new_dst;
-}
-
-void wear_inventory_category_on_avatar_step2( BOOL proceed, LLUUID category, BOOL append, BOOL follow_folder_links )
-{
-	// Find all the wearables that are in the category's subtree.	
-	lldebugs << "wear_inventory_category_on_avatar_step2()" << llendl;
-	if(proceed)
-	{
-		LLInventoryModel::cat_array_t cat_array;
-		LLInventoryModel::item_array_t item_array;
-		LLFindWearables is_wearable;
-		gInventory.collectDescendentsIf(category,
-										cat_array,
-										item_array,
-										LLInventoryModel::EXCLUDE_TRASH,
-										is_wearable,
-										follow_folder_links);
-		S32 i;
-		S32 wearable_count = item_array.count();
-
-		LLInventoryModel::cat_array_t	obj_cat_array;
-		LLInventoryModel::item_array_t	obj_item_array;
-		LLIsType is_object( LLAssetType::AT_OBJECT );
-		gInventory.collectDescendentsIf(category,
-										obj_cat_array,
-										obj_item_array,
-										LLInventoryModel::EXCLUDE_TRASH,
-										is_object,
-										follow_folder_links);
-		S32 obj_count = obj_item_array.count();
-
-		// Find all gestures in this folder
-		LLInventoryModel::cat_array_t	gest_cat_array;
-		LLInventoryModel::item_array_t	gest_item_array;
-		LLIsType is_gesture( LLAssetType::AT_GESTURE );
-		gInventory.collectDescendentsIf(category,
-										gest_cat_array,
-										gest_item_array,
-										LLInventoryModel::EXCLUDE_TRASH,
-										is_gesture,
-										follow_folder_links);
-		S32 gest_count = gest_item_array.count();
-
-		if( !wearable_count && !obj_count && !gest_count)
-		{
-			LLNotifications::instance().add("CouldNotPutOnOutfit");
-			return;
-		}
-		
-		const LLUUID &current_outfit_id = gInventory.findCategoryUUIDForType(LLAssetType::AT_CURRENT_OUTFIT);
-		// Processes that take time should show the busy cursor
-		inc_busy_count();
-		
-		LLInventoryModel::cat_array_t co_cat_array;
-		LLInventoryModel::item_array_t co_item_array;
-		gInventory.collectDescendents(current_outfit_id, co_cat_array, co_item_array,
-									  LLInventoryModel::EXCLUDE_TRASH);
-		if (append)
-		{
-			// Remove duplicates and update counts.
-			removeDuplicateItems(item_array,co_item_array);
-			wearable_count = item_array.count();
-
-			removeDuplicateItems(obj_item_array,co_item_array);
-			obj_count = obj_item_array.count();
-
-			removeDuplicateItems(gest_item_array,co_item_array);
-			gest_count = gest_item_array.count();
-		}
-			
-
-		if (wearable_count > 0 || obj_count > 0)
-		{
-			if (!append) 
-			{
-				// Remove all current outfit folder links if we're now replacing the contents.
-				for (i = 0; i < co_item_array.count(); ++i)
-				{
-					gInventory.purgeObject(co_item_array.get(i)->getUUID());
-				}
-			}
-		}
-
-		// Activate all gestures in this folder
-		if (gest_count > 0)
-		{
-			llinfos << "Activating " << gest_count << " gestures" << llendl;
-
-			LLGestureManager::instance().activateGestures(gest_item_array);
-
-			// Update the inventory item labels to reflect the fact
-			// they are active.
-			LLViewerInventoryCategory* catp = gInventory.getCategory(category);
-			if (catp)
-			{
-				gInventory.updateCategory(catp);
-				gInventory.notifyObservers();
-			}
-		}
-
-		if(wearable_count > 0)
-		{
-			// Note: can't do normal iteration, because if all the
-			// wearables can be resolved immediately, then the
-			// callback will be called (and this object deleted)
-			// before the final getNextData().
-			LLWearableHoldingPattern* holder = new LLWearableHoldingPattern;
-			LLFoundData* found;
-			LLDynamicArray<LLFoundData*> found_container;
-			for(i = 0; i  < wearable_count; ++i)
-			{
-				found = new LLFoundData(item_array.get(i)->getUUID(),
-										item_array.get(i)->getAssetUUID(),
-										item_array.get(i)->getName(),
-										item_array.get(i)->getType());
-				holder->mFoundList.push_front(found);
-				found_container.put(found);
-			}
-			for(i = 0; i < wearable_count; ++i)
-			{
-				gAddToOutfit = append;
-				found = found_container.get(i);
-				
-				// Populate the current outfit folder with links to the newly added wearables
-				std::string link_name = "WearableLink";
-				link_inventory_item(gAgent.getID(), found->mItemID, current_outfit_id, link_name,
-									LLAssetType::AT_LINK, LLPointer<LLInventoryCallback>(NULL));
-
-				// Fetch the wearables about to be worn.
-				LLWearableList::instance().getAsset(found->mAssetID,
-													found->mName,
-													found->mAssetType,
-													wear_inventory_category_on_avatar_loop,
-													(void*)holder);
-			}
-		}
-
-
-		//If not appending and the folder doesn't contain only gestures, take off all attachments.
-		if (!append 
-			&& !(wearable_count == 0 && obj_count == 0 && gest_count > 0) )
-		{
-			LLAgentWearables::userRemoveAllAttachments(NULL);
-		}
-
-		if( obj_count > 0 )
-		{
-			// We've found some attachements.  Add these.
-
-			LLVOAvatar* avatar = gAgent.getAvatarObject();
-			if( avatar )
-			{
-				// Build a compound message to send all the objects that need to be rezzed.
-
-				// Limit number of packets to send
-				const S32 MAX_PACKETS_TO_SEND = 10;
-				const S32 OBJECTS_PER_PACKET = 4;
-				const S32 MAX_OBJECTS_TO_SEND = MAX_PACKETS_TO_SEND * OBJECTS_PER_PACKET;
-				if( obj_count > MAX_OBJECTS_TO_SEND )
-				{
-					obj_count = MAX_OBJECTS_TO_SEND;
-				}
-				
-				// Create an id to keep the parts of the compound message together
-				LLUUID compound_msg_id;
-				compound_msg_id.generate();
-				LLMessageSystem* msg = gMessageSystem;
-
-				for(i = 0; i < obj_count; ++i)
-				{
-					if( 0 == (i % OBJECTS_PER_PACKET) )
-					{
-						// Start a new message chunk
-						msg->newMessageFast(_PREHASH_RezMultipleAttachmentsFromInv);
-						msg->nextBlockFast(_PREHASH_AgentData);
-						msg->addUUIDFast(_PREHASH_AgentID, gAgent.getID());
-						msg->addUUIDFast(_PREHASH_SessionID, gAgent.getSessionID());
-						msg->nextBlockFast(_PREHASH_HeaderData);
-						msg->addUUIDFast(_PREHASH_CompoundMsgID, compound_msg_id );
-						msg->addU8Fast(_PREHASH_TotalObjects, obj_count );
-						msg->addBOOLFast(_PREHASH_FirstDetachAll, !append );
-					}
-
-					const LLInventoryItem* item = obj_item_array.get(i).get();
-					msg->nextBlockFast(_PREHASH_ObjectData );
-					msg->addUUIDFast(_PREHASH_ItemID, item->getLinkedUUID());
-					msg->addUUIDFast(_PREHASH_OwnerID, item->getPermissions().getOwner());
-					msg->addU8Fast(_PREHASH_AttachmentPt, 0 );	// Wear at the previous or default attachment point
-					pack_permissions_slam(msg, item->getFlags(), item->getPermissions());
-					msg->addStringFast(_PREHASH_Name, item->getName());
-					msg->addStringFast(_PREHASH_Description, item->getDescription());
-
-					if( (i+1 == obj_count) || ((OBJECTS_PER_PACKET-1) == (i % OBJECTS_PER_PACKET)) )
-					{
-						// End of message chunk
-						msg->sendReliable( gAgent.getRegion()->getHost() );
-					}
-
-				}
-
-				for(i = 0; i < obj_count; ++i)
-				{
-					const std::string link_name = "AttachmentLink";
-					const LLInventoryItem* item = obj_item_array.get(i).get();
-					link_inventory_item(gAgent.getID(), item->getLinkedUUID(), current_outfit_id, link_name,
-										LLAssetType::AT_LINK, LLPointer<LLInventoryCallback>(NULL));
-				}
-			}
-		}
-
-		// In the particular case that we're switching to a different outfit,
-		// create a link to the folder that we wore.
-		LLViewerInventoryCategory* catp = gInventory.getCategory(category);
-		if (!append && catp && catp->getPreferredType() == LLAssetType::AT_OUTFIT)
-		{
-			link_inventory_item(gAgent.getID(), category, current_outfit_id, catp->getName(),
-								LLAssetType::AT_LINK_FOLDER, LLPointer<LLInventoryCallback>(NULL));
-		}
-	}
-}
-
-void wear_inventory_category_on_avatar_loop(LLWearable* wearable, void* data)
-{
-	LLWearableHoldingPattern* holder = (LLWearableHoldingPattern*)data;
-	BOOL append= gAddToOutfit;
-	
-	if(wearable)
-	{
-		for (LLWearableHoldingPattern::found_list_t::iterator iter = holder->mFoundList.begin();
-			 iter != holder->mFoundList.end(); ++iter)
-		{
-			LLFoundData* data = *iter;
-			if(wearable->getAssetID() == data->mAssetID)
-			{
-				data->mWearable = wearable;
-				break;
-			}
-		}
-	}
-	holder->mResolved += 1;
-	if(holder->mResolved >= (S32)holder->mFoundList.size())
-	{
-		wear_inventory_category_on_avatar_step3(holder, append);
-	}
-}
-
-void wear_inventory_category_on_avatar_step3(LLWearableHoldingPattern* holder, BOOL append)
-{
-	lldebugs << "wear_inventory_category_on_avatar_step3()" << llendl;
-	LLInventoryItem::item_array_t items;
-	LLDynamicArray< LLWearable* > wearables;
-
-	// For each wearable type, find the first instance in the category
-	// that we recursed through.
-	for( S32 i = 0; i < WT_COUNT; i++ )
-	{
-		for (LLWearableHoldingPattern::found_list_t::iterator iter = holder->mFoundList.begin();
-			 iter != holder->mFoundList.end(); ++iter)
-		{
-			LLFoundData* data = *iter;
-			LLWearable* wearable = data->mWearable;
-			if( wearable && ((S32)wearable->getType() == i) )
-			{
-				LLViewerInventoryItem* item;
-				item = (LLViewerInventoryItem*)gInventory.getItem(data->mItemID);
-				if( item && (item->getAssetUUID() == wearable->getAssetID()) )
-				{
-					items.put(item);
-					wearables.put(wearable);
-				}
-				break;
-			}
-		}
-	}
-
-	if(wearables.count() > 0)
-	{
-		gAgentWearables.setWearableOutfit(items, wearables, !append);
-		gInventory.notifyObservers();
-	}
-
-	delete holder;
-
-	dec_busy_count();
-}
-
 void remove_inventory_category_from_avatar( LLInventoryCategory* category )
 {
 	if(!category) return;
@@ -4945,6 +4369,7 @@ void LLWearableBridge::onWearOnAvatarArrived( LLWearable* wearable, void* userda
 }
 
 // static
+// BAP remove the "add" code path once everything is fully COF-ified.
 void LLWearableBridge::onWearAddOnAvatarArrived( LLWearable* wearable, void* userdata )
 {
 	LLUUID* item_id = (LLUUID*) userdata;
@@ -5304,15 +4729,15 @@ void LLWearableBridgeAction::wearOnAvatar()
 }
 
 //virtual 
-void	LLWearableBridgeAction::doIt()
+void LLWearableBridgeAction::doIt()
 {
-	if( isInTrash() )
+	if(isInTrash())
 	{
 		LLNotifications::instance().add("CannotWearTrash");
 	}
 	else if(isAgentInventory())
 	{
-		if( !gAgentWearables.isWearingItem( mUUID ) )
+		if(!gAgentWearables.isWearingItem(mUUID))
 		{
 			wearOnAvatar();
 		}
diff --git a/indra/newview/llinventorybridge.h b/indra/newview/llinventorybridge.h
index 25859b7f73b..bc950520aa7 100644
--- a/indra/newview/llinventorybridge.h
+++ b/indra/newview/llinventorybridge.h
@@ -200,9 +200,8 @@ class LLInvFVBridge : public LLFolderViewEventListener
 	BOOL isInTrash() const;
 	BOOL isLinkedObjectInTrash() const; // Is this obj or its baseobj in the trash?
 
-	// return true if the item is in agent inventory. if false, it
-	// must be lost or in the inventory library.
-	BOOL isAgentInventory() const;
+	BOOL isAgentInventory() const; // false if lost or in the inventory library
+	BOOL isCOFFolder() const; // true if COF or descendent of.
 	virtual BOOL isItemPermissive() const;
 	static void changeItemParent(LLInventoryModel* model,
 								 LLViewerInventoryItem* item,
@@ -295,6 +294,7 @@ class LLFolderBridge : public LLInvFVBridge
 	virtual BOOL isItemMovable();
 	virtual BOOL isUpToDate() const;
 	virtual BOOL isItemCopyable() const;
+	virtual BOOL isClipboardPasteableAsLink() const;
 	virtual BOOL copyToClipboard() const;
 	
 	static void createWearable(LLFolderBridge* bridge, EWearableType type);
@@ -769,8 +769,6 @@ class LLWearableBridgeAction: public LLInvFVBridgeAction
 };
 
 void wear_inventory_item_on_avatar(LLInventoryItem* item);
-void wear_outfit_by_name(const std::string& name);
-void wear_inventory_category(LLInventoryCategory* category, bool copy, bool append);
 
 class LLViewerJointAttachment;
 void rez_attachment(LLViewerInventoryItem* item, LLViewerJointAttachment* attachment);
@@ -787,4 +785,5 @@ BOOL move_inv_category_world_to_agent(const LLUUID& object_id,
 
 void teleport_via_landmark(const LLUUID& asset_id);
 
+
 #endif // LL_LLINVENTORYBRIDGE_H
diff --git a/indra/newview/llinventoryfilter.h b/indra/newview/llinventoryfilter.h
index 670b1f000b9..b803df110b4 100644
--- a/indra/newview/llinventoryfilter.h
+++ b/indra/newview/llinventoryfilter.h
@@ -94,6 +94,7 @@ class LLInventoryFilter
 	BOOL isModified();
 	BOOL isModifiedAndClear();
 	BOOL isSinceLogoff();
+	bool hasFilterString() { return mFilterSubString.size() > 0; }
 	void clearModified() { mModified = FALSE; mFilterBehavior = FILTER_NONE; }
 	const std::string getName() const { return mName; }
 	std::string getFilterText();
diff --git a/indra/newview/llinventorymodel.cpp b/indra/newview/llinventorymodel.cpp
index 8062da0dfe7..4cbf3e169b5 100644
--- a/indra/newview/llinventorymodel.cpp
+++ b/indra/newview/llinventorymodel.cpp
@@ -187,9 +187,9 @@ LLInventoryModel::~LLInventoryModel()
 // This is a convenience function to check if one object has a parent
 // chain up to the category specified by UUID.
 BOOL LLInventoryModel::isObjectDescendentOf(const LLUUID& obj_id,
-											const LLUUID& cat_id)
+											const LLUUID& cat_id) const
 {
-	LLInventoryObject* obj = getObject(obj_id);
+	const LLInventoryObject* obj = getObject(obj_id);
 	while(obj)
 	{
 		const LLUUID& parent_id = obj->getParentUUID();
@@ -307,13 +307,13 @@ void LLInventoryModel::unlockDirectDescendentArrays(const LLUUID& cat_id)
 // inventory category on the fly if one does not exist.
 LLUUID LLInventoryModel::findCategoryUUIDForType(LLAssetType::EType t, bool create_folder)
 {
-	LLUUID rv = findCatUUID(t);
+	const LLUUID &rv = findCatUUID(t);
 	if(rv.isNull() && isInventoryUsable() && create_folder)
 	{
-		LLUUID root_id = gInventory.getRootFolderID();
+		const LLUUID &root_id = gInventory.getRootFolderID();
 		if(root_id.notNull())
 		{
-			rv = createNewCategory(root_id, t, LLStringUtil::null);
+			return createNewCategory(root_id, t, LLStringUtil::null);
 		}
 	}
 	return rv;
@@ -321,9 +321,9 @@ LLUUID LLInventoryModel::findCategoryUUIDForType(LLAssetType::EType t, bool crea
 
 // Internal method which looks for a category with the specified
 // preferred type. Returns LLUUID::null if not found.
-LLUUID LLInventoryModel::findCatUUID(LLAssetType::EType preferred_type)
+const LLUUID &LLInventoryModel::findCatUUID(LLAssetType::EType preferred_type) const
 {
-	LLUUID root_id = gInventory.getRootFolderID();
+	const LLUUID &root_id = gInventory.getRootFolderID();
 	if(LLAssetType::AT_CATEGORY == preferred_type)
 	{
 		return root_id;
@@ -440,7 +440,7 @@ void LLInventoryModel::collectDescendentsIf(const LLUUID& id,
 	// Start with categories
 	if(!include_trash)
 	{
-		LLUUID trash_id(findCatUUID(LLAssetType::AT_TRASH));
+		const LLUUID trash_id = findCategoryUUIDForType(LLAssetType::AT_TRASH);
 		if(trash_id.notNull() && (trash_id == id))
 			return;
 	}
@@ -473,8 +473,7 @@ void LLInventoryModel::collectDescendentsIf(const LLUUID& id,
 			item = item_array->get(i);
 			if (item->getActualType() == LLAssetType::AT_LINK_FOLDER)
 			{
-				// BAP either getLinkedCategory() should return non-const, or the functor should take const.
-				LLViewerInventoryCategory *linked_cat = const_cast<LLViewerInventoryCategory*>(item->getLinkedCategory());
+				LLViewerInventoryCategory *linked_cat = item->getLinkedCategory();
 				if (linked_cat)
 				{
 					if(add(linked_cat,NULL))
@@ -548,10 +547,10 @@ void LLInventoryModel::collectLinkedItems(const LLUUID& id,
 
 // Generates a string containing the path to the item specified by
 // item_id.
-void LLInventoryModel::appendPath(const LLUUID& id, std::string& path)
+void LLInventoryModel::appendPath(const LLUUID& id, std::string& path) const
 {
 	std::string temp;
-	LLInventoryObject* obj = getObject(id);
+	const LLInventoryObject* obj = getObject(id);
 	LLUUID parent_id;
 	if(obj) parent_id = obj->getParentUUID();
 	std::string forward_slash("/");
@@ -567,7 +566,7 @@ void LLInventoryModel::appendPath(const LLUUID& id, std::string& path)
 	path.append(temp);
 }
 
-bool LLInventoryModel::isInventoryUsable()
+bool LLInventoryModel::isInventoryUsable() const
 {
 	bool result = false;
 	if(gInventory.getRootFolderID().notNull() && mIsAgentInvUsable)
@@ -1086,7 +1085,7 @@ void LLInventoryModel::removeObserver(LLInventoryObserver* observer)
 	mObservers.erase(observer);
 }
 
-BOOL LLInventoryModel::containsObserver(LLInventoryObserver* observer)
+BOOL LLInventoryModel::containsObserver(LLInventoryObserver* observer) const
 {
 	return mObservers.find(observer) != mObservers.end();
 }
@@ -1791,6 +1790,7 @@ void LLInventoryModel::addItem(LLViewerInventoryItem* item)
 		if (item->getIsBrokenLink())
 		{
 			llwarns << "Add link item without baseobj present ( name: " << item->getName() << " itemID: " << item->getUUID() << " assetID: " << item->getAssetUUID() << " )  parent: " << item->getParentUUID() << llendl;
+//			llassert_always(FALSE); // DO NOT MERGE THIS IN.  This is an AVP debugging line.  If this line triggers, it means that you just loaded in a broken link.  Unless that happens because you actually deleted a baseobj without deleting the link, it's indicative of a serious problem (likely with your inventory) and should be diagnosed.
 		}
 		mItemMap[item->getUUID()] = item;
 		//mInventory[item->getUUID()] = item;
@@ -1817,7 +1817,7 @@ void LLInventoryModel::empty()
 	//mInventory.clear();
 }
 
-void LLInventoryModel::accountForUpdate(const LLCategoryUpdate& update)
+void LLInventoryModel::accountForUpdate(const LLCategoryUpdate& update) const
 {
 	LLViewerInventoryCategory* cat = getCategory(update.mCategoryID);
 	if(cat)
@@ -2177,7 +2177,8 @@ bool LLInventoryModel::loadSkeleton(
 				update_map_t::const_iterator the_count = child_counts.find(cat->getUUID());
 				if(the_count != no_child_counts)
 				{
-					cat->setDescendentCount((*the_count).second.mValue);
+					const S32 num_descendents = (*the_count).second.mValue;
+					cat->setDescendentCount(num_descendents);
 				}
 				else
 				{
@@ -2481,7 +2482,7 @@ void LLInventoryModel::buildParentChildMap()
 		}
 	}
 
-	LLUUID agent_inv_root_id = gInventory.getRootFolderID();
+	const LLUUID &agent_inv_root_id = gInventory.getRootFolderID();
 	if (agent_inv_root_id.notNull())
 	{
 		cat_array_t* catsp = get_ptr_in_map(mParentChildCategoryTree, agent_inv_root_id);
@@ -3324,7 +3325,7 @@ void LLInventoryModel::emptyFolderType(const std::string notification, LLAssetTy
 void LLInventoryModel::removeItem(const LLUUID& item_id)
 {
 	LLViewerInventoryItem* item = getItem(item_id);
-	const LLUUID& new_parent = findCategoryUUIDForType(LLAssetType::AT_TRASH);
+	const LLUUID new_parent = findCategoryUUIDForType(LLAssetType::AT_TRASH);
 	if (item && item->getParentUUID() != new_parent)
 	{
 		LLInventoryModel::update_list_t update;
@@ -3342,7 +3343,7 @@ void LLInventoryModel::removeItem(const LLUUID& item_id)
 	}
 }
 
-LLUUID LLInventoryModel::getRootFolderID() const
+const LLUUID &LLInventoryModel::getRootFolderID() const
 {
 	return mRootFolderID;
 }
@@ -3352,7 +3353,7 @@ void LLInventoryModel::setRootFolderID(const LLUUID& val)
 	mRootFolderID = val;
 }
 
-LLUUID LLInventoryModel::getLibraryRootFolderID() const
+const LLUUID &LLInventoryModel::getLibraryRootFolderID() const
 {
 	return mLibraryRootFolderID;
 }
@@ -3362,7 +3363,7 @@ void LLInventoryModel::setLibraryRootFolderID(const LLUUID& val)
 	mLibraryRootFolderID = val;
 }
 
-LLUUID LLInventoryModel::getLibraryOwnerID() const
+const LLUUID &LLInventoryModel::getLibraryOwnerID() const
 {
 	return mLibraryOwnerID;
 }
@@ -3375,13 +3376,13 @@ void LLInventoryModel::setLibraryOwnerID(const LLUUID& val)
 //----------------------------------------------------------------------------
 
 // *NOTE: DEBUG functionality
-void LLInventoryModel::dumpInventory()
+void LLInventoryModel::dumpInventory() const
 {
 	llinfos << "\nBegin Inventory Dump\n**********************:" << llendl;
-	llinfos << "mCategroy[] contains " << mCategoryMap.size() << " items." << llendl;
-	for(cat_map_t::iterator cit = mCategoryMap.begin(); cit != mCategoryMap.end(); ++cit)
+	llinfos << "mCategory[] contains " << mCategoryMap.size() << " items." << llendl;
+	for(cat_map_t::const_iterator cit = mCategoryMap.begin(); cit != mCategoryMap.end(); ++cit)
 	{
-		LLViewerInventoryCategory* cat = cit->second;
+		const LLViewerInventoryCategory* cat = cit->second;
 		if(cat)
 		{
 			llinfos << "  " <<  cat->getUUID() << " '" << cat->getName() << "' "
@@ -3394,9 +3395,9 @@ void LLInventoryModel::dumpInventory()
 		}
 	}	
 	llinfos << "mItemMap[] contains " << mItemMap.size() << " items." << llendl;
-	for(item_map_t::iterator iit = mItemMap.begin(); iit != mItemMap.end(); ++iit)
+	for(item_map_t::const_iterator iit = mItemMap.begin(); iit != mItemMap.end(); ++iit)
 	{
-		LLViewerInventoryItem* item = iit->second;
+		const LLViewerInventoryItem* item = iit->second;
 		if(item)
 		{
 			llinfos << "  " << item->getUUID() << " "
diff --git a/indra/newview/llinventorymodel.h b/indra/newview/llinventorymodel.h
index 2ddc35b9ef3..e3e4f6aca0f 100644
--- a/indra/newview/llinventorymodel.h
+++ b/indra/newview/llinventorymodel.h
@@ -99,12 +99,12 @@ class LLInventoryCollectFunctor;
 class LLInventoryModel
 {
 public:
-	typedef enum e_has_children
+	enum EHasChildren
 	{
 		CHILDREN_NO,
 		CHILDREN_YES,
 		CHILDREN_MAYBE
-	}EHasChildren;
+	};
 
 	// These are used a lot...
 	typedef LLDynamicArray<LLPointer<LLViewerInventoryCategory> > cat_array_t;
@@ -133,7 +133,7 @@ class LLInventoryModel
 
 	// This is a convenience function to check if one object has a
 	// parent chain up to the category specified by UUID.
-	BOOL isObjectDescendentOf(const LLUUID& obj_id, const LLUUID& cat_id);
+	BOOL isObjectDescendentOf(const LLUUID& obj_id, const LLUUID& cat_id) const;
 
 	// Get the object by id. Returns NULL if not found.
 	// * WARNING: use the pointer returned for read operations - do
@@ -197,7 +197,7 @@ class LLInventoryModel
 
 	// The inventory model usage is sensitive to the initial construction of the 
 	// model. 
-	bool isInventoryUsable();
+	bool isInventoryUsable() const;
 
 	//
 	// Mutators
@@ -261,7 +261,7 @@ class LLInventoryModel
 	// to remove it.
 	void addObserver(LLInventoryObserver* observer);
 	void removeObserver(LLInventoryObserver* observer);
-	BOOL containsObserver(LLInventoryObserver* observer);
+	BOOL containsObserver(LLInventoryObserver* observer) const;
 
 	//
 	// Misc Methods 
@@ -312,7 +312,7 @@ class LLInventoryModel
 
 	// Generates a string containing the path to the item specified by
 	// item_id.
-	void appendPath(const LLUUID& id, std::string& path);
+	void appendPath(const LLUUID& id, std::string& path) const;
 
 	// message handling functionality
 	static void registerCallbacks(LLMessageSystem* msg);
@@ -370,7 +370,7 @@ class LLInventoryModel
 	// Call these methods when there are category updates, but call
 	// them *before* the actual update so the method can do descendent
 	// accounting correctly.
-	void accountForUpdate(const LLCategoryUpdate& update);
+	void accountForUpdate(const LLCategoryUpdate& update) const;
 	void accountForUpdate(const update_list_t& updates);
 	void accountForUpdate(const update_map_t& updates);
 
@@ -404,9 +404,9 @@ class LLInventoryModel
 	// gInventory is a singleton and represents the agent's inventory.
 	// The "library" is actually the inventory of a special agent,
 	// usually Alexandria Linden.
-	LLUUID getRootFolderID() const;
-	LLUUID getLibraryOwnerID() const;
-	LLUUID getLibraryRootFolderID() const;
+	const LLUUID &getRootFolderID() const;
+	const LLUUID &getLibraryOwnerID() const;
+	const LLUUID &getLibraryRootFolderID() const;
 
 	// These are set during login with data from the server
 	void setRootFolderID(const LLUUID& id);
@@ -422,9 +422,13 @@ class LLInventoryModel
 	void addCategory(LLViewerInventoryCategory* category);
 	void addItem(LLViewerInventoryItem* item);
 
+	// ! DEPRECRATE ! Remove this and add it into findCategoryUUIDForType,
+	// since that's the only function that uses this.  It's too confusing 
+	// having both methods.
+	// 
 	// Internal method which looks for a category with the specified
 	// preferred type. Returns LLUUID::null if not found
- 	LLUUID findCatUUID(LLAssetType::EType preferred_type);
+ 	const LLUUID &findCatUUID(LLAssetType::EType preferred_type) const;
 
 	// Empty the entire contents
 	void empty();
@@ -515,7 +519,7 @@ class LLInventoryModel
 
 public:
 	// *NOTE: DEBUG functionality
-	void dumpInventory();
+	void dumpInventory() const;
 	static bool isBulkFetchProcessingComplete();
 	static void stopBackgroundFetch(); // stop fetch process
 
diff --git a/indra/newview/llstartup.cpp b/indra/newview/llstartup.cpp
index 8fb0f201cb0..44e76a0bc11 100644
--- a/indra/newview/llstartup.cpp
+++ b/indra/newview/llstartup.cpp
@@ -188,6 +188,7 @@
 #include "llagentlanguage.h"
 #include "llwearable.h"
 #include "llinventorybridge.h"
+#include "llappearancemgr.h"
 
 #if LL_WINDOWS
 #include "llwindebug.h"
@@ -3345,10 +3346,10 @@ void LLStartUp::loadInitialOutfit( const std::string& outfit_folder_name,
 	}
 	else
 	{
-		wear_outfit_by_name(outfit_folder_name);
+		LLAppearanceManager::wearOutfitByName(outfit_folder_name);
 	}
-	wear_outfit_by_name(gestures);
-	wear_outfit_by_name(COMMON_GESTURES_FOLDER);
+	LLAppearanceManager::wearOutfitByName(gestures);
+	LLAppearanceManager::wearOutfitByName(COMMON_GESTURES_FOLDER);
 
 	// This is really misnamed -- it means we have started loading
 	// an outfit/shape that will give the avatar a gender eventually. JC
diff --git a/indra/newview/lltooldraganddrop.cpp b/indra/newview/lltooldraganddrop.cpp
index b35208cd031..08040cfaa5e 100644
--- a/indra/newview/lltooldraganddrop.cpp
+++ b/indra/newview/lltooldraganddrop.cpp
@@ -73,6 +73,7 @@
 #include "llimview.h"
 #include "llrootview.h"
 #include "llagentui.h"
+#include "llappearancemgr.h"
 
 // MAX ITEMS is based on (sizeof(uuid)+2) * count must be < MTUBYTES
 // or 18 * count < 1200 => count < 1200/18 => 66. I've cut it down a
@@ -2508,7 +2509,7 @@ EAcceptance LLToolDragAndDrop::dad3dWearCategory(
 		if(drop)
 		{
 		    BOOL append = ( (mask & MASK_SHIFT) ? TRUE : FALSE );
-			wear_inventory_category(category, false, append);
+			LLAppearanceManager::wearInventoryCategory(category, false, append);
 		}
 		return ACCEPT_YES_MULTI;
 	}
@@ -2516,7 +2517,7 @@ EAcceptance LLToolDragAndDrop::dad3dWearCategory(
 	{
 		if(drop)
 		{
-			wear_inventory_category(category, true, false);
+			LLAppearanceManager::wearInventoryCategory(category, true, false);
 		}
 		return ACCEPT_YES_MULTI;
 	}
diff --git a/indra/newview/llviewerinventory.cpp b/indra/newview/llviewerinventory.cpp
index ec20af46a18..f3557bb8f73 100644
--- a/indra/newview/llviewerinventory.cpp
+++ b/indra/newview/llviewerinventory.cpp
@@ -53,6 +53,7 @@
 #include "llpreviewgesture.h"
 #include "llviewerwindow.h"
 #include "lltrans.h"
+#include "llappearancemgr.h"
 
 ///----------------------------------------------------------------------------
 /// Local function declarations, constants, enums, and typedefs
@@ -760,6 +761,11 @@ void WearOnAvatarCallback::fire(const LLUUID& inv_item)
 	}
 }
 
+void ModifiedCOFCallback::fire(const LLUUID& inv_item)
+{
+	LLAppearanceManager::instance().updateAppearanceFromCOF();
+}
+
 RezAttachmentCallback::RezAttachmentCallback(LLViewerJointAttachment *attachmentp)
 {
 	mAttach = attachmentp;
@@ -874,6 +880,27 @@ void link_inventory_item(
 	const LLAssetType::EType asset_type,
 	LLPointer<LLInventoryCallback> cb)
 {
+	const LLInventoryObject *baseobj = gInventory.getObject(item_id);
+	if (!baseobj)
+	{
+		llwarns << "attempt to link to unknown item, linked-to-item's itemID " << item_id << llendl;
+		return;
+	}
+	if (baseobj && baseobj->getIsLinkType())
+	{
+		llwarns << "attempt to create a link to a link, linked-to-item's itemID " << item_id << llendl;
+		return;
+	}
+
+	if (baseobj && !LLAssetType::lookupCanLink(baseobj->getType()))
+	{
+		// Fail if item can be found but is of a type that can't be linked.
+		// Arguably should fail if the item can't be found too, but that could
+		// be a larger behavioral change.
+		llwarns << "attempt to link an unlinkable item, type = " << baseobj->getActualType() << llendl;
+		return;
+	}
+	
 	LLMessageSystem* msg = gMessageSystem;
 	msg->newMessageFast(_PREHASH_LinkInventoryItem);
 	msg->nextBlockFast(_PREHASH_AgentData);
@@ -1291,21 +1318,26 @@ bool LLViewerInventoryItem::getIsBrokenLink() const
 	return LLAssetType::lookupIsLinkType(getType());
 }
 
-const LLViewerInventoryItem *LLViewerInventoryItem::getLinkedItem() const
+LLViewerInventoryItem *LLViewerInventoryItem::getLinkedItem() const
 {
 	if (mType == LLAssetType::AT_LINK)
 	{
-		const LLViewerInventoryItem *linked_item = gInventory.getItem(mAssetUUID);
+		LLViewerInventoryItem *linked_item = gInventory.getItem(mAssetUUID);
+		if (linked_item && linked_item->getIsLinkType())
+		{
+			llwarns << "Warning: Accessing link to link" << llendl;
+			return NULL;
+		}
 		return linked_item;
 	}
 	return NULL;
 }
 
-const LLViewerInventoryCategory *LLViewerInventoryItem::getLinkedCategory() const
+LLViewerInventoryCategory *LLViewerInventoryItem::getLinkedCategory() const
 {
 	if (mType == LLAssetType::AT_LINK_FOLDER)
 	{
-		const LLViewerInventoryCategory *linked_category = gInventory.getCategory(mAssetUUID);
+		LLViewerInventoryCategory *linked_category = gInventory.getCategory(mAssetUUID);
 		return linked_category;
 	}
 	return NULL;
diff --git a/indra/newview/llviewerinventory.h b/indra/newview/llviewerinventory.h
index c4ff30bbfc4..631f9ac48b9 100644
--- a/indra/newview/llviewerinventory.h
+++ b/indra/newview/llviewerinventory.h
@@ -149,8 +149,8 @@ class LLViewerInventoryItem : public LLInventoryItem, public boost::signals2::tr
 	LLTransactionID getTransactionID() const { return mTransactionID; }
 	
 	bool getIsBrokenLink() const; // true if the baseitem this points to doesn't exist in memory.
-	const LLViewerInventoryItem *getLinkedItem() const;
-	const LLViewerInventoryCategory *getLinkedCategory() const;
+	LLViewerInventoryItem *getLinkedItem() const;
+	LLViewerInventoryCategory *getLinkedCategory() const;
 
 	// callback
 	void onCallingCardNameLookup(const LLUUID& id, const std::string& first_name, const std::string& last_name);
@@ -234,6 +234,11 @@ class WearOnAvatarCallback : public LLInventoryCallback
 	void fire(const LLUUID& inv_item);
 };
 
+class ModifiedCOFCallback : public LLInventoryCallback
+{
+	void fire(const LLUUID& inv_item);
+};
+
 class LLViewerJointAttachment;
 
 class RezAttachmentCallback : public LLInventoryCallback
diff --git a/indra/newview/llviewermenu.cpp b/indra/newview/llviewermenu.cpp
index 338bb7ad7c4..ac3defef01a 100644
--- a/indra/newview/llviewermenu.cpp
+++ b/indra/newview/llviewermenu.cpp
@@ -212,6 +212,7 @@
 #include "llfloaternotificationsconsole.h"
 
 #include "lltexlayer.h"
+#include "llappearancemgr.h"
 
 using namespace LLVOAvatarDefines;
 
@@ -6375,13 +6376,13 @@ void handle_selected_texture_info(void*)
 
 void handle_test_male(void*)
 {
-	wear_outfit_by_name("Male Shape & Outfit");
+	LLAppearanceManager::wearOutfitByName("Male Shape & Outfit");
 	//gGestureList.requestResetFromServer( TRUE );
 }
 
 void handle_test_female(void*)
 {
-	wear_outfit_by_name("Female Shape & Outfit");
+	LLAppearanceManager::wearOutfitByName("Female Shape & Outfit");
 	//gGestureList.requestResetFromServer( FALSE );
 }
 
-- 
GitLab