diff --git a/indra/llui/llview.cpp b/indra/llui/llview.cpp
index c091686ffb91c1f4174d02b36231d58225e47c0d..bd56da912165cd861819d2f25ae4331b7298a5d0 100644
--- a/indra/llui/llview.cpp
+++ b/indra/llui/llview.cpp
@@ -1319,8 +1319,6 @@ void LLView::drawChildren()
 
 			if (viewp->getVisible() && viewp->getRect().isValid())
 			{
-				// check for bad data
-				llassert_always(viewp->getVisible() == TRUE);
 				// Only draw views that are within the root view
 				localRectToScreen(viewp->getRect(),&screenRect);
 				if ( rootRect.overlaps(screenRect)  && LLUI::sDirtyRect.overlaps(screenRect))
diff --git a/indra/newview/llagentwearables.cpp b/indra/newview/llagentwearables.cpp
index 6b5e43973ea6c2551be2867260e41d83ff7fd217..68c4fa1ea0d619817fa327bf98c2728637eecae7 100644
--- a/indra/newview/llagentwearables.cpp
+++ b/indra/newview/llagentwearables.cpp
@@ -49,6 +49,7 @@
 #include "llmd5.h"
 #include "llnotificationsutil.h"
 #include "llpaneloutfitsinventory.h"
+#include "llsidepanelappearance.h"
 #include "llsidetray.h"
 #include "lltexlayer.h"
 #include "llviewerregion.h"
@@ -539,9 +540,15 @@ void LLAgentWearables::setWearableName(const LLUUID& item_id, const std::string&
 BOOL LLAgentWearables::isWearableModifiable(LLWearableType::EType type, U32 index) const
 {
 	LLUUID item_id = getWearableItemID(type, index);
-	if (!item_id.isNull())
+	return item_id.notNull() ? isWearableModifiable(item_id) : FALSE;
+}
+
+BOOL LLAgentWearables::isWearableModifiable(const LLUUID& item_id) const
+{
+	const LLUUID& linked_id = gInventory.getLinkedItemID(item_id);
+	if (linked_id.notNull())
 	{
-		LLInventoryItem* item = gInventory.getItem(item_id);
+		LLInventoryItem* item = gInventory.getItem(linked_id);
 		if (item && item->getPermissions().allowModifyBy(gAgent.getID(),
 														 gAgent.getGroupID()))
 		{
@@ -595,12 +602,13 @@ LLInventoryItem* LLAgentWearables::getWearableInventoryItem(LLWearableType::ETyp
 
 const LLWearable* LLAgentWearables::getWearableFromItemID(const LLUUID& item_id) const
 {
+	const LLUUID& base_item_id = gInventory.getLinkedItemID(item_id);
 	for (S32 i=0; i < LLWearableType::WT_COUNT; i++)
 	{
 		for (U32 j=0; j < getWearableCount((LLWearableType::EType)i); j++)
 		{
 			const LLWearable * curr_wearable = getWearable((LLWearableType::EType)i, j);
-			if (curr_wearable && (curr_wearable->getItemID() == item_id))
+			if (curr_wearable && (curr_wearable->getItemID() == base_item_id))
 			{
 				return curr_wearable;
 			}
@@ -812,6 +820,16 @@ LLWearable* LLAgentWearables::getTopWearable(const LLWearableType::EType type)
 	return getWearable(type, count-1);
 }
 
+LLWearable* LLAgentWearables::getBottomWearable(const LLWearableType::EType type)
+{
+	if (getWearableCount(type) == 0)
+	{
+		return NULL;
+	}
+
+	return getWearable(type, 0);
+}
+
 U32 LLAgentWearables::getWearableCount(const LLWearableType::EType type) const
 {
 	wearableentry_map_t::const_iterator wearable_iter = mWearableDatas.find(type);
@@ -860,12 +878,7 @@ const LLUUID LLAgentWearables::getWearableAssetID(LLWearableType::EType type, U3
 
 BOOL LLAgentWearables::isWearingItem(const LLUUID& item_id) const
 {
-	const LLUUID& base_item_id = gInventory.getLinkedItemID(item_id);
-	if (getWearableFromItemID(base_item_id) != NULL) 
-	{
-		return TRUE;
-	}
-	return FALSE;
+	return getWearableFromItemID(item_id) != NULL;
 }
 
 // MULTI-WEARABLE: DEPRECATED (see backwards compatibility)
@@ -1862,6 +1875,20 @@ void LLAgentWearables::checkWearablesLoaded() const
 #endif
 }
 
+// Returns false if the given wearable is already topmost/bottommost
+// (depending on closer_to_body parameter).
+bool LLAgentWearables::canMoveWearable(const LLUUID& item_id, bool closer_to_body)
+{
+	const LLWearable* wearable = getWearableFromItemID(item_id);
+	if (!wearable) return false;
+
+	LLWearableType::EType wtype = wearable->getType();
+	const LLWearable* marginal_wearable = closer_to_body ? getBottomWearable(wtype) : getTopWearable(wtype);
+	if (!marginal_wearable) return false;
+
+	return wearable != marginal_wearable;
+}
+
 BOOL LLAgentWearables::areWearablesLoaded() const
 {
 	checkWearablesLoaded();
@@ -1932,6 +1959,23 @@ bool LLAgentWearables::moveWearable(const LLViewerInventoryItem* item, bool clos
 	return false;
 }
 
+// static
+void LLAgentWearables::editWearable(const LLUUID& item_id)
+{
+	LLViewerInventoryItem* item;
+	LLWearable* wearable;
+
+	if ((item = gInventory.getLinkedItem(item_id)) &&
+		(wearable = gAgentWearables.getWearableFromAssetID(item->getAssetUUID())) &&
+		gAgentWearables.isWearableModifiable(item->getUUID()) &&
+		item->isFinished())
+	{
+		LLPanel* panel = LLSideTray::getInstance()->showPanel("panel_outfit_edit", LLSD());
+		// copied from LLPanelOutfitEdit::onEditWearableClicked()
+		LLSidepanelAppearance::editWearable(wearable, panel->getParent());
+	}
+}
+
 void LLAgentWearables::updateServer()
 {
 	sendAgentWearablesUpdate();
diff --git a/indra/newview/llagentwearables.h b/indra/newview/llagentwearables.h
index 734bd9fd47aacde710b8dc39de8afecc4d203c55..1f19d1045b25a1f9392e2bc3512089d8c028cfe6 100644
--- a/indra/newview/llagentwearables.h
+++ b/indra/newview/llagentwearables.h
@@ -71,17 +71,18 @@ class LLAgentWearables
 public:
 	BOOL			isWearingItem(const LLUUID& item_id) const;
 	BOOL			isWearableModifiable(LLWearableType::EType type, U32 index /*= 0*/) const;
+	BOOL			isWearableModifiable(const LLUUID& item_id) const;
+
 	BOOL			isWearableCopyable(LLWearableType::EType type, U32 index /*= 0*/) const;
 	BOOL			areWearablesLoaded() const;
 	void			updateWearablesLoaded();
 	void			checkWearablesLoaded() const;
+	bool			canMoveWearable(const LLUUID& item_id, bool closer_to_body);
 	
 	// Note: False for shape, skin, eyes, and hair, unless you have MORE than 1.
 	bool			canWearableBeRemoved(const LLWearable* wearable) const;
 
 	void			animateAllWearableParams(F32 delta, BOOL upload_bake);
-	
-	bool			moveWearable(const LLViewerInventoryItem* item, bool closer_to_body);
 
 	//--------------------------------------------------------------------
 	// Accessors
@@ -96,6 +97,7 @@ class LLAgentWearables
 	LLWearable*			getWearable(const LLWearableType::EType type, U32 index /*= 0*/); 
 	const LLWearable* 	getWearable(const LLWearableType::EType type, U32 index /*= 0*/) const;
 	LLWearable*		getTopWearable(const LLWearableType::EType type);
+	LLWearable*		getBottomWearable(const LLWearableType::EType type);
 	U32				getWearableCount(const LLWearableType::EType type) const;
 	U32				getWearableCount(const U32 tex_index) const;
 
@@ -133,6 +135,14 @@ class LLAgentWearables
 	void			recoverMissingWearable(const LLWearableType::EType type, U32 index /*= 0*/);
 	void			recoverMissingWearableDone();
 
+	//--------------------------------------------------------------------
+	// Editing/moving wearables
+	//--------------------------------------------------------------------
+
+public:
+	static void		editWearable(const LLUUID& item_id);
+	bool			moveWearable(const LLViewerInventoryItem* item, bool closer_to_body);
+
 	//--------------------------------------------------------------------
 	// Removing wearables
 	//--------------------------------------------------------------------
diff --git a/indra/newview/llappearancemgr.cpp b/indra/newview/llappearancemgr.cpp
index dcef86a5fc425ade6c40d4040dbb71c056d4b113..2eb7cfd34aead2020d83e0387b1f42351fa723e8 100644
--- a/indra/newview/llappearancemgr.cpp
+++ b/indra/newview/llappearancemgr.cpp
@@ -152,27 +152,6 @@ class LLUpdateDirtyState: public LLInventoryCallback
 };
 
 
-//Inventory collect functor collecting wearables of a specific wearable type
-class LLFindClothesOfType : public LLInventoryCollectFunctor
-{
-public:
-	LLFindClothesOfType(LLWearableType::EType type) : mWearableType(type) {}
-	virtual ~LLFindClothesOfType() {}
-	virtual bool operator()(LLInventoryCategory* cat, LLInventoryItem* item)
-	{
-		if (!item) return false;
-		if (item->getType() != LLAssetType::AT_CLOTHING) return false;
-		
-		LLViewerInventoryItem *vitem = dynamic_cast<LLViewerInventoryItem*>(item);
-		if (!vitem || vitem->getWearableType() != mWearableType) return false;
-
-		return true;
-	}
-
-	const LLWearableType::EType mWearableType;
-};
-
-
 LLUpdateAppearanceOnDestroy::LLUpdateAppearanceOnDestroy():
 	mFireCount(0)
 {
@@ -671,7 +650,7 @@ const LLUUID LLAppearanceMgr::getBaseOutfitUUID()
 	return outfit_cat->getUUID();
 }
 
-bool LLAppearanceMgr::wearItemOnAvatar(const LLUUID& item_id_to_wear, bool do_update)
+bool LLAppearanceMgr::wearItemOnAvatar(const LLUUID& item_id_to_wear, bool do_update, bool replace)
 {
 	if (item_id_to_wear.isNull()) return false;
 
@@ -692,6 +671,14 @@ bool LLAppearanceMgr::wearItemOnAvatar(const LLUUID& item_id_to_wear, bool do_up
 			LLNotificationsUtil::add("CanNotChangeAppearanceUntilLoaded");
 			return false;
 		}
+
+		// Remove the existing wearables of the same type.
+		// Remove existing body parts anyway because we must not be able to wear e.g. two skins.
+		if (replace || item_to_wear->getType() == LLAssetType::AT_BODYPART)
+		{
+			removeCOFLinksOfType(item_to_wear->getWearableType(), false);
+		}
+
 		addCOFItemLink(item_to_wear, do_update);
 		break;
 	case LLAssetType::AT_OBJECT:
@@ -711,6 +698,35 @@ void LLAppearanceMgr::changeOutfit(bool proceed, const LLUUID& category, bool ap
 	LLAppearanceMgr::instance().updateCOF(category,append);
 }
 
+void LLAppearanceMgr::replaceCurrentOutfit(const LLUUID& new_outfit)
+{
+	LLViewerInventoryCategory* cat = gInventory.getCategory(new_outfit);
+	wearInventoryCategory(cat, false, false);
+}
+
+void LLAppearanceMgr::addCategoryToCurrentOutfit(const LLUUID& cat_id)
+{
+	LLViewerInventoryCategory* cat = gInventory.getCategory(cat_id);
+	wearInventoryCategory(cat, false, true);
+}
+
+void LLAppearanceMgr::takeOffOutfit(const LLUUID& cat_id)
+{
+	LLInventoryModel::cat_array_t cats;
+	LLInventoryModel::item_array_t items;
+	LLFindWearables collector;
+
+	gInventory.collectDescendentsIf(cat_id, cats, items, FALSE, collector);
+
+	LLInventoryModel::item_array_t::const_iterator it = items.begin();
+	const LLInventoryModel::item_array_t::const_iterator it_end = items.end();
+	for( ; it_end != it; ++it)
+	{
+		LLViewerInventoryItem* item = *it;
+		removeItemFromAvatar(item->getUUID());
+	}
+}
+
 // Create a copy of src_id + contents as a subfolder of dst_id.
 void LLAppearanceMgr::shallowCopyCategory(const LLUUID& src_id, const LLUUID& dst_id,
 											  LLPointer<LLInventoryCallback> cb)
@@ -1563,6 +1579,29 @@ void LLAppearanceMgr::removeCOFItemLinks(const LLUUID& item_id, bool do_update)
 	}
 }
 
+void LLAppearanceMgr::removeCOFLinksOfType(LLWearableType::EType type, bool do_update)
+{
+	LLFindWearablesOfType filter_wearables_of_type(type);
+	LLInventoryModel::cat_array_t cats;
+	LLInventoryModel::item_array_t items;
+	LLInventoryModel::item_array_t::const_iterator it;
+
+	gInventory.collectDescendentsIf(getCOF(), cats, items, true, filter_wearables_of_type);
+	for (it = items.begin(); it != items.end(); ++it)
+	{
+		const LLViewerInventoryItem* item = *it;
+		if (item->getIsLinkType()) // we must operate on links only
+		{
+			gInventory.purgeObject(item->getUUID());
+		}
+	}
+
+	if (do_update)
+	{
+		updateAppearanceFromCOF();
+	}
+}
+
 bool sort_by_linked_uuid(const LLViewerInventoryItem* item1, const LLViewerInventoryItem* item2)
 {
 	if (!item1 || !item2)
@@ -1893,7 +1932,6 @@ void LLAppearanceMgr::removeItemFromAvatar(const LLUUID& id_to_remove)
 	}
 }
 
-
 bool LLAppearanceMgr::moveWearable(LLViewerInventoryItem* item, bool closer_to_body)
 {
 	if (!item || !item->isWearableType()) return false;
@@ -1902,11 +1940,11 @@ bool LLAppearanceMgr::moveWearable(LLViewerInventoryItem* item, bool closer_to_b
 
 	LLInventoryModel::cat_array_t cats;
 	LLInventoryModel::item_array_t items;
-	LLFindClothesOfType filter_wearables_of_type(item->getWearableType());
+	LLFindWearablesOfType filter_wearables_of_type(item->getWearableType());
 	gInventory.collectDescendentsIf(getCOF(), cats, items, true, filter_wearables_of_type);
 	if (items.empty()) return false;
 
-	//*TODO all items are not guarantied to have valid descriptions (check?)
+	// We assume that the items have valid descriptions.
 	std::sort(items.begin(), items.end(), WearablesOrderComparator(item->getWearableType()));
 
 	if (closer_to_body && items.front() == item) return false;
diff --git a/indra/newview/llappearancemgr.h b/indra/newview/llappearancemgr.h
index 516dada39da484c95787d501ec7818c9e868b383..96541beb7d84672a81488972af2b3fe2abd11d83 100644
--- a/indra/newview/llappearancemgr.h
+++ b/indra/newview/llappearancemgr.h
@@ -58,6 +58,9 @@ class LLAppearanceMgr: public LLSingleton<LLAppearanceMgr>
 	void wearCategoryFinal(LLUUID& cat_id, bool copy_items, bool append);
 	void wearOutfitByName(const std::string& name);
 	void changeOutfit(bool proceed, const LLUUID& category, bool append);
+	void replaceCurrentOutfit(const LLUUID& new_outfit);
+	void takeOffOutfit(const LLUUID& cat_id);
+	void addCategoryToCurrentOutfit(const LLUUID& cat_id);
 
 	// Copy all items and the src category itself.
 	void shallowCopyCategory(const LLUUID& src_id, const LLUUID& dst_id,
@@ -81,7 +84,7 @@ class LLAppearanceMgr: public LLSingleton<LLAppearanceMgr>
 	const LLUUID getBaseOutfitUUID();
 
 	// Wear/attach an item (from a user's inventory) on the agent
-	bool wearItemOnAvatar(const LLUUID& item_to_wear, bool do_update = true);
+	bool wearItemOnAvatar(const LLUUID& item_to_wear, bool do_update = true, bool replace = false);
 
 	// Update the displayed outfit name in UI.
 	void updatePanelOutfitName(const std::string& name);
@@ -111,6 +114,7 @@ class LLAppearanceMgr: public LLSingleton<LLAppearanceMgr>
 
 	// Remove COF entries
 	void removeCOFItemLinks(const LLUUID& item_id, bool do_update = true);
+	void removeCOFLinksOfType(LLWearableType::EType type, bool do_update = true);
 
 	// Add COF link to ensemble folder.
 	void addEnsembleLink(LLInventoryCategory* item, bool do_update = true);
diff --git a/indra/newview/llcofwearables.cpp b/indra/newview/llcofwearables.cpp
index 47862ad9219072ac14cc05759b0b2e249755f31b..dfc203111ab7ea5a982bc9651ab6ec33ee50dd48 100644
--- a/indra/newview/llcofwearables.cpp
+++ b/indra/newview/llcofwearables.cpp
@@ -61,6 +61,11 @@ class CofAttachmentContextMenu : public LLListContextMenu
 
 	/*virtual*/ LLContextMenu* createMenu()
 	{
+		LLUICtrl::CommitCallbackRegistry::ScopedRegistrar registrar;
+
+		functor_t take_off = boost::bind(&LLAppearanceMgr::removeItemFromAvatar, LLAppearanceMgr::getInstance(), _1);
+		registrar.add("Attachment.Detach", boost::bind(handleMultiple, take_off, mUUIDs));
+
 		return createFromFile("menu_cof_attachment.xml");
 	}
 };
@@ -73,8 +78,49 @@ class CofClothingContextMenu : public LLListContextMenu
 
 	/*virtual*/ LLContextMenu* createMenu()
 	{
+		LLUICtrl::CommitCallbackRegistry::ScopedRegistrar registrar;
+		LLUICtrl::EnableCallbackRegistry::ScopedRegistrar enable_registrar;
+		LLUUID selected_id = mUUIDs.back();
+		functor_t take_off = boost::bind(&LLAppearanceMgr::removeItemFromAvatar, LLAppearanceMgr::getInstance(), _1);
+
+		registrar.add("Clothing.TakeOff", boost::bind(handleMultiple, take_off, mUUIDs));
+		registrar.add("Clothing.MoveUp", boost::bind(moveWearable, selected_id, false));
+		registrar.add("Clothing.MoveDown", boost::bind(moveWearable, selected_id, true));
+		registrar.add("Clothing.Edit", boost::bind(LLAgentWearables::editWearable, selected_id));
+
+		enable_registrar.add("Clothing.OnEnable", boost::bind(&CofClothingContextMenu::onEnable, this, _2));
+
 		return createFromFile("menu_cof_clothing.xml");
 	}
+
+	bool onEnable(const LLSD& data)
+	{
+		std::string param = data.asString();
+		LLUUID selected_id = mUUIDs.back();
+
+		if ("move_up" == param)
+		{
+			return gAgentWearables.canMoveWearable(selected_id, false);
+		}
+		else if ("move_down" == param)
+		{
+			return gAgentWearables.canMoveWearable(selected_id, true);
+		}
+		else if ("edit" == param)
+		{
+			return gAgentWearables.isWearableModifiable(selected_id);
+		}
+		return true;
+	}
+
+	// We don't use LLAppearanceMgr::moveWearable() directly because
+	// the item may be invalidated between setting the callback and calling it.
+	static bool moveWearable(const LLUUID& item_id, bool closer_to_body)
+	{
+		LLViewerInventoryItem* item = gInventory.getItem(item_id);
+		return LLAppearanceMgr::instance().moveWearable(item, closer_to_body);
+	}
+
 };
 
 //////////////////////////////////////////////////////////////////////////
@@ -85,8 +131,31 @@ class CofBodyPartContextMenu : public LLListContextMenu
 
 	/*virtual*/ LLContextMenu* createMenu()
 	{
+		LLUICtrl::CommitCallbackRegistry::ScopedRegistrar registrar;
+		LLUICtrl::EnableCallbackRegistry::ScopedRegistrar enable_registrar;
+		LLUUID selected_id = mUUIDs.back();
+
+		registrar.add("BodyPart.Replace", boost::bind(&LLAppearanceMgr::wearItemOnAvatar,
+			LLAppearanceMgr::getInstance(), selected_id, true, true));
+		registrar.add("BodyPart.Edit", boost::bind(LLAgentWearables::editWearable, selected_id));
+
+		enable_registrar.add("BodyPart.OnEnable", boost::bind(&CofBodyPartContextMenu::onEnable, this, _2));
+
 		return createFromFile("menu_cof_body_part.xml");
 	}
+
+	bool onEnable(const LLSD& data)
+	{
+		std::string param = data.asString();
+		LLUUID selected_id = mUUIDs.back();
+
+		if ("edit" == param)
+		{
+			return gAgentWearables.isWearableModifiable(selected_id);
+		}
+
+		return true;
+	}
 };
 
 //////////////////////////////////////////////////////////////////////////
diff --git a/indra/newview/llinventorybridge.cpp b/indra/newview/llinventorybridge.cpp
index 2d27c890740ad0dd2c3dfc0498fa56a90e60526c..35f7cbcd0188d7eba1bc656f0111ae6f9d793cc8 100644
--- a/indra/newview/llinventorybridge.cpp
+++ b/indra/newview/llinventorybridge.cpp
@@ -222,9 +222,7 @@ void LLInvFVBridge::cutToClipboard()
 // *TODO: make sure this does the right thing
 void LLInvFVBridge::showProperties()
 {
-	LLSD key;
-	key["id"] = mUUID;
-	LLSideTray::getInstance()->showPanel("sidepanel_inventory", key);
+	show_item_profile(mUUID);
 
 	// Disable old properties floater; this is replaced by the sidepanel.
 	/*
@@ -4150,9 +4148,7 @@ void LLObjectBridge::openItem()
 		LLInvFVBridgeAction::doAction(item->getType(),mUUID,getInventoryModel());
 	}
 
-	LLSD key;
-	key["id"] = mUUID;
-	LLSideTray::getInstance()->showPanel("sidepanel_inventory", key);
+	show_item_profile(mUUID);
 
 	// Disable old properties floater; this is replaced by the sidepanel.
 	/*
@@ -4434,7 +4430,7 @@ void wear_inventory_item_on_avatar( LLInventoryItem* item )
 		lldebugs << "wear_inventory_item_on_avatar( " << item->getName()
 				 << " )" << llendl;
 
-		LLAppearanceMgr::instance().addCOFItemLink(item);
+		LLAppearanceMgr::getInstance()->wearItemOnAvatar(item->getUUID(), true, false);
 	}
 }
 
@@ -4892,8 +4888,7 @@ void LLWearableBridge::onEditOnAvatar(void* user_data)
 
 void LLWearableBridge::editOnAvatar()
 {
-	LLUUID linked_id = gInventory.getLinkedItemID(mUUID);
-	const LLWearable* wearable = gAgentWearables.getWearableFromItemID(linked_id);
+	const LLWearable* wearable = gAgentWearables.getWearableFromItemID(mUUID);
 	if( wearable )
 	{
 		// Set the tab to the right wearable.
@@ -4983,7 +4978,7 @@ void LLWearableBridge::removeAllClothesFromAvatar()
 				gAgentWearables.getWearableInventoryItem((LLWearableType::EType)itype, index));
 			if (!item)
 				continue;
-			const LLUUID &item_id = gInventory.getLinkedItemID(item->getUUID());
+			const LLUUID &item_id = item->getUUID();
 			const LLWearable *wearable = gAgentWearables.getWearableFromItemID(item_id);
 			if (!wearable)
 				continue;
diff --git a/indra/newview/llinventoryfunctions.cpp b/indra/newview/llinventoryfunctions.cpp
index 2b4d9fb25ca1eba3c430a1a2f1b722cfce224f29..c38d45f0f5fae660e6719f22d0bb0f1d5c8262de 100644
--- a/indra/newview/llinventoryfunctions.cpp
+++ b/indra/newview/llinventoryfunctions.cpp
@@ -74,6 +74,7 @@
 #include "llscrollbar.h"
 #include "llscrollcontainer.h"
 #include "llselectmgr.h"
+#include "llsidetray.h"
 #include "lltabcontainer.h"
 #include "lltooldraganddrop.h"
 #include "lluictrlfactory.h"
@@ -160,6 +161,19 @@ BOOL get_is_item_worn(const LLUUID& id)
 	return FALSE;
 }
 
+void show_item_profile(const LLUUID& item_uuid)
+{
+	LLUUID linked_uuid = gInventory.getLinkedItemID(item_uuid);
+	LLSideTray::getInstance()->showPanel("sidepanel_inventory", LLSD().with("id", linked_uuid));
+}
+
+void show_item_original(const LLUUID& item_uuid)
+{
+	LLInventoryPanel* active_panel = LLInventoryPanel::getActiveInventoryPanel();
+	if (!active_panel) return;
+	active_panel->setSelection(gInventory.getLinkedItemID(item_uuid), TAKE_FOCUS_NO);
+}
+
 ///----------------------------------------------------------------------------
 /// LLInventoryCollectFunctor implementations
 ///----------------------------------------------------------------------------
@@ -343,6 +357,21 @@ bool LLFindWearables::operator()(LLInventoryCategory* cat,
 	return FALSE;
 }
 
+bool LLFindWearablesOfType::operator()(LLInventoryCategory* cat, LLInventoryItem* item)
+{
+	if (!item) return false;
+	if (item->getType() != LLAssetType::AT_CLOTHING &&
+		item->getType() != LLAssetType::AT_BODYPART)
+	{
+		return false;
+	}
+
+	LLViewerInventoryItem *vitem = dynamic_cast<LLViewerInventoryItem*>(item);
+	if (!vitem || vitem->getWearableType() != mWearableType) return false;
+
+	return true;
+}
+
 ///----------------------------------------------------------------------------
 /// LLAssetIDMatches 
 ///----------------------------------------------------------------------------
diff --git a/indra/newview/llinventoryfunctions.h b/indra/newview/llinventoryfunctions.h
index 79b9b4a9ccf322c860abe0e45ed337f70bf0b095..8b96ba29d943bd74712464f2b3a987fafb7fa98b 100644
--- a/indra/newview/llinventoryfunctions.h
+++ b/indra/newview/llinventoryfunctions.h
@@ -46,6 +46,9 @@
 // Is this item or its baseitem is worn, attached, etc...
 BOOL get_is_item_worn(const LLUUID& id);
 
+void show_item_profile(const LLUUID& item_uuid);
+
+void show_item_original(const LLUUID& item_uuid);
 
 void change_item_parent(LLInventoryModel* model,
 									 LLViewerInventoryItem* item,
@@ -262,6 +265,17 @@ class LLFindWearables : public LLInventoryCollectFunctor
 							LLInventoryItem* item);
 };
 
+//Inventory collect functor collecting wearables of a specific wearable type
+class LLFindWearablesOfType : public LLInventoryCollectFunctor
+{
+public:
+	LLFindWearablesOfType(LLWearableType::EType type) : mWearableType(type) {}
+	virtual ~LLFindWearablesOfType() {}
+	virtual bool operator()(LLInventoryCategory* cat, LLInventoryItem* item);
+
+	const LLWearableType::EType mWearableType;
+};
+
 /**                    Inventory Collector Functions
  **                                                                            **
  *******************************************************************************/
diff --git a/indra/newview/llinventorymodel.cpp b/indra/newview/llinventorymodel.cpp
index c373512aced6f15786ecc1f78723fbd7b43f2012..23df6b6cc571a5735c0b331e4543d8701cde20ee 100644
--- a/indra/newview/llinventorymodel.cpp
+++ b/indra/newview/llinventorymodel.cpp
@@ -566,6 +566,11 @@ const LLUUID& LLInventoryModel::getLinkedItemID(const LLUUID& object_id) const
 	return item->getLinkedUUID();
 }
 
+LLViewerInventoryItem* LLInventoryModel::getLinkedItem(const LLUUID& object_id) const
+{
+	return object_id.notNull() ? getItem(getLinkedItemID(object_id)) : NULL;
+}
+
 LLInventoryModel::item_array_t LLInventoryModel::collectLinkedItems(const LLUUID& id,
 																	const LLUUID& start_folder_id)
 {
diff --git a/indra/newview/llinventorymodel.h b/indra/newview/llinventorymodel.h
index 1f7bd50328acf080c67cd6108e85c33ffc7e4a5c..7b56d0bdd11bee5a5d68214336049ec4f6fad98c 100644
--- a/indra/newview/llinventorymodel.h
+++ b/indra/newview/llinventorymodel.h
@@ -258,6 +258,7 @@ class LLInventoryModel
 
 	// Get the inventoryID or item that this item points to, else just return object_id
 	const LLUUID& getLinkedItemID(const LLUUID& object_id) const;
+	LLViewerInventoryItem* getLinkedItem(const LLUUID& object_id) const;
 private:
 	mutable LLPointer<LLViewerInventoryItem> mLastItem; // cache recent lookups	
 
diff --git a/indra/newview/lloutfitslist.cpp b/indra/newview/lloutfitslist.cpp
index 36832c9d1666e2421b3b59f7f4673789816371a9..17a2db7a43353db1f1a81d99fc8b6eb45adc19cf 100644
--- a/indra/newview/lloutfitslist.cpp
+++ b/indra/newview/lloutfitslist.cpp
@@ -38,6 +38,7 @@
 
 #include "llaccordionctrl.h"
 #include "llaccordionctrltab.h"
+#include "llagentwearables.h"
 #include "llappearancemgr.h"
 #include "llinventoryfunctions.h"
 #include "llinventorymodel.h"
@@ -55,6 +56,20 @@ class OutfitContextMenu : public LLListContextMenu
 protected:
 	/* virtual */ LLContextMenu* createMenu()
 	{
+		LLUICtrl::CommitCallbackRegistry::ScopedRegistrar registrar;
+		LLUICtrl::EnableCallbackRegistry::ScopedRegistrar enable_registrar;
+		LLUUID selected_id = mUUIDs.front();
+
+		registrar.add("Outfit.WearReplace",
+			boost::bind(&LLAppearanceMgr::replaceCurrentOutfit, &LLAppearanceMgr::instance(), selected_id));
+		registrar.add("Outfit.WearAdd",
+			boost::bind(&LLAppearanceMgr::addCategoryToCurrentOutfit, &LLAppearanceMgr::instance(), selected_id));
+		registrar.add("Outfit.TakeOff",
+				boost::bind(&LLAppearanceMgr::takeOffOutfit, &LLAppearanceMgr::instance(), selected_id));
+		// *TODO: implement this
+		// registrar.add("Outfit.Rename", boost::bind());
+		// registrar.add("Outfit.Delete", boost::bind());
+
 		return createFromFile("menu_outfit_tab.xml");
 	}
 };
diff --git a/indra/newview/llpanelobjectinventory.cpp b/indra/newview/llpanelobjectinventory.cpp
index 39ade409674a4a299f156319388edabef3a0f54d..0d3beaa9a5808fab325913437f20400468ff93a0 100644
--- a/indra/newview/llpanelobjectinventory.cpp
+++ b/indra/newview/llpanelobjectinventory.cpp
@@ -174,11 +174,7 @@ LLInventoryItem* LLTaskInvFVBridge::findItem() const
 
 void LLTaskInvFVBridge::showProperties()
 {
-	LLSD key;
-	key["object"] = mPanel->getTaskUUID();
-	key["id"] = mUUID;
-	LLSideTray::getInstance()->showPanel("sidepanel_inventory", key);
-
+	show_item_profile(mUUID);
 
 	// Disable old properties floater; this is replaced by the sidepanel.
 	/*
diff --git a/indra/newview/llwearableitemslist.cpp b/indra/newview/llwearableitemslist.cpp
index 5836252eacc45d704c399dfade77299ad6297eff..fb7577c00835e82aced109d14aa97833469c3f76 100644
--- a/indra/newview/llwearableitemslist.cpp
+++ b/indra/newview/llwearableitemslist.cpp
@@ -36,6 +36,7 @@
 #include "lliconctrl.h"
 
 #include "llagentwearables.h"
+#include "llappearancemgr.h"
 #include "llinventoryfunctions.h"
 #include "llinventorymodel.h"
 #include "llmenugl.h" // for LLContextMenu
@@ -433,7 +434,120 @@ void LLWearableItemsList::onRightClick(S32 x, S32 y)
 // virtual
 LLContextMenu* LLWearableItemsList::ContextMenu::createMenu()
 {
-	return createFromFile("menu_wearable_list_item.xml");
+	LLUICtrl::CommitCallbackRegistry::ScopedRegistrar registrar;
+	const uuid_vec_t& ids = mUUIDs;		// selected items IDs
+	LLUUID selected_id = ids.front();	// ID of the first selected item
+
+	functor_t wear = boost::bind(&LLAppearanceMgr::wearItemOnAvatar, LLAppearanceMgr::getInstance(), _1, true, false);
+	functor_t take_off = boost::bind(&LLAppearanceMgr::removeItemFromAvatar, LLAppearanceMgr::getInstance(), _1);
+
+	// Register handlers common for all wearable types.
+	registrar.add("Wearable.Wear", boost::bind(handleMultiple, wear, ids));
+	registrar.add("Wearable.Edit", boost::bind(handleMultiple, LLAgentWearables::editWearable, ids));
+	registrar.add("Wearable.ShowOriginal", boost::bind(show_item_original, selected_id));
+
+	// Register handlers for clothing.
+	registrar.add("Clothing.TakeOff", boost::bind(handleMultiple, take_off, ids));
+
+	// Register handlers for body parts.
+
+	// Register handlers for attachments.
+	registrar.add("Attachment.Detach", boost::bind(handleMultiple, take_off, ids));
+	registrar.add("Attachment.Profile", boost::bind(show_item_profile, selected_id));
+
+	// Create the menu.
+	LLContextMenu* menu = createFromFile("menu_wearable_list_item.xml");
+
+	// Determine which items should be visible/enabled.
+	updateItemsVisibility(menu);
+	return menu;
+}
+
+void LLWearableItemsList::ContextMenu::updateItemsVisibility(LLContextMenu* menu)
+{
+	if (!menu)
+	{
+		llwarns << "Invalid menu" << llendl;
+		return;
+	}
+
+	const uuid_vec_t& ids = mUUIDs;	// selected items IDs
+	U32 mask = 0;					// mask of selected items' types
+	U32 nitems = ids.size();		// number of selected items
+	U32 nworn = 0;					// number of worn items among the selected ones
+	U32 nwornlinks = 0;				// number of worn links among the selected items
+	U32 neditable = 0;				// number of editable items among the selected ones
+
+	for (uuid_vec_t::const_iterator it = ids.begin(); it != ids.end(); ++it)
+	{
+		LLUUID id = *it;
+		LLViewerInventoryItem* item = gInventory.getItem(id);
+
+		if (!item)
+		{
+			llwarns << "Invalid item" << llendl;
+			// *NOTE: the logic below may not work in this case
+			continue;
+		}
+
+		updateMask(mask, item->getType());
+
+		bool is_link = item->getIsLinkType();
+		bool is_worn = get_is_item_worn(id);
+		bool is_editable = gAgentWearables.isWearableModifiable(id);
+
+		if (is_worn)
+		{
+			++nworn;
+
+			if (is_link)
+			{
+				++nwornlinks;
+			}
+		}
+		if (is_editable)
+		{
+			++neditable;
+		}
+	} // for
+
+	// *TODO: eliminate multiple traversals over the menu items
+	// *TODO: try disabling items rather than hiding them
+	// *FIX:  we may hide *all* items and thus get an ugly empty menu
+	setMenuItemVisible(menu, "wear",			nworn == 0);
+	setMenuItemVisible(menu, "edit",			mask & (MASK_CLOTHING|MASK_BODYPART) && nitems == 1 && neditable == 1);
+	setMenuItemVisible(menu, "show_original",	nitems == 1 && nwornlinks == nitems);
+	setMenuItemVisible(menu, "take_off",		mask == MASK_CLOTHING && nworn == nitems); // selected only worn clothes
+	setMenuItemVisible(menu, "detach",			mask == MASK_ATTACHMENT && nworn == nitems);
+	setMenuItemVisible(menu, "object_profile",	mask == MASK_ATTACHMENT && nitems == 1);
+}
+
+// We need this method to convert non-zero BOOL values to exactly 1 (TRUE).
+// Otherwise code relying on a BOOL value being TRUE may fail
+// (I experienced a weird assert in LLView::drawChildren() because of that.
+void LLWearableItemsList::ContextMenu::setMenuItemVisible(LLContextMenu* menu, const std::string& name, bool val)
+{
+	menu->setItemVisible(name, val);
+}
+
+void LLWearableItemsList::ContextMenu::updateMask(U32& mask, LLAssetType::EType at)
+{
+	if (at == LLAssetType::AT_CLOTHING)
+	{
+		mask |= MASK_CLOTHING;
+	}
+	else if (at == LLAssetType::AT_BODYPART)
+	{
+		mask |= MASK_BODYPART;
+	}
+	else if (at == LLAssetType::AT_OBJECT)
+	{
+		mask |= MASK_ATTACHMENT;
+	}
+	else
+	{
+		llwarns << "Unsupported asset type: " << at << llendl;
+	}
 }
 
 // EOF
diff --git a/indra/newview/llwearableitemslist.h b/indra/newview/llwearableitemslist.h
index d7b09ca9348e55a2f9b2a75266a1df7c346debd7..7ad1b5a3ad804cc554d78e90844e87a571f33d72 100644
--- a/indra/newview/llwearableitemslist.h
+++ b/indra/newview/llwearableitemslist.h
@@ -287,7 +287,16 @@ class LLWearableItemsList : public LLInventoryItemsList
 	class ContextMenu : public LLListContextMenu, public LLSingleton<ContextMenu>
 	{
 	protected:
+		enum {
+			MASK_CLOTHING		= 0x01,
+			MASK_BODYPART		= 0x02,
+			MASK_ATTACHMENT		= 0x04,
+		};
+
 		/* virtual */ LLContextMenu* createMenu();
+		void updateItemsVisibility(LLContextMenu* menu);
+		void setMenuItemVisible(LLContextMenu* menu, const std::string& name, bool val);
+		void updateMask(U32& mask, LLAssetType::EType at);
 	};
 
 	struct Params : public LLInitParam::Block<Params, LLInventoryItemsList::Params>
diff --git a/indra/newview/skins/default/xui/en/panel_outfit_edit.xml b/indra/newview/skins/default/xui/en/panel_outfit_edit.xml
index b473a7a282b38b7d10646166c063263e88d29ad2..895cc4e3ccd4269bc31502a7380e115a7d766f20 100644
--- a/indra/newview/skins/default/xui/en/panel_outfit_edit.xml
+++ b/indra/newview/skins/default/xui/en/panel_outfit_edit.xml
@@ -336,6 +336,7 @@
 		              allow_select="true"
 		              layout="topleft"
 		              follows="all"
+		              multi_select="true"
 		              width="310"
 		              height="140"
 		              left="0"