From 9f996443604a902e03de87d2d14545b4adffa69c Mon Sep 17 00:00:00 2001
From: Vadim Savchuk <vsavchuk@productengine.com>
Date: Thu, 17 Jun 2010 19:03:57 +0300
Subject: [PATCH] EXT-7722 Fixed "Add to COF" and "Remove from COF" outfit
 context menu items to be enabled when appropriate and work properly.

Work on "Take Off - Remove from Current Outfit" and "Wear - Add to Current Outfit" menu options:
- The menu items of the outfit context menu and the My Outfits gear menu are now disabled
  when inappropriate instead of being hidden.
- The menu items get enabled/disabled depending on whether you can wear (take off) anything from the selected outfit.
  (was: depending on whether you're wearing the outfit)
- Changed the way the options work: they now only operate on clothes and attachments.
  "Add to COF" now only adds those body parts that are missing in COF;
  "Remove from COF" doesn't touch body parts at all.

  Without this change both "Add" and "Remove" options would be available simultaneously, because
  any valid outfit contains body parts. I think that would be confusing.
  And you don't expect you body parts to be replaced when doing "Add to COF'. (that's addition, not replacement)

Reviewed by Mike Antipov at https://codereview.productengine.com/secondlife/r/585/

--HG--
branch : product-engine
---
 indra/newview/llappearancemgr.cpp             | 33 ++++++++++++++++++-
 indra/newview/llappearancemgr.h               |  6 ++++
 indra/newview/llinventorybridge.cpp           | 15 +--------
 indra/newview/llinventorybridge.h             |  1 -
 indra/newview/llinventoryfunctions.cpp        | 30 ++++++++++++++---
 indra/newview/llinventoryfunctions.h          | 24 +++++++++-----
 indra/newview/lloutfitslist.cpp               | 16 ++++-----
 indra/newview/llpaneloutfitsinventory.cpp     |  8 ++---
 .../skins/default/xui/en/menu_outfit_tab.xml  |  6 ++++
 9 files changed, 97 insertions(+), 42 deletions(-)

diff --git a/indra/newview/llappearancemgr.cpp b/indra/newview/llappearancemgr.cpp
index be58a562c0c..4e96372da93 100644
--- a/indra/newview/llappearancemgr.cpp
+++ b/indra/newview/llappearancemgr.cpp
@@ -1068,7 +1068,7 @@ void LLAppearanceMgr::takeOffOutfit(const LLUUID& cat_id)
 {
 	LLInventoryModel::cat_array_t cats;
 	LLInventoryModel::item_array_t items;
-	LLFindWorn collector;
+	LLFindWearablesEx collector(/*is_worn=*/ true, /*include_body_parts=*/ false);
 
 	gInventory.collectDescendentsIf(cat_id, cats, items, FALSE, collector);
 
@@ -1224,6 +1224,34 @@ bool LLAppearanceMgr::getCanRemoveOutfit(const LLUUID& outfit_cat_id)
 	return true;
 }
 
+// static
+bool LLAppearanceMgr::getCanRemoveFromCOF(const LLUUID& outfit_cat_id)
+{
+	LLInventoryModel::cat_array_t cats;
+	LLInventoryModel::item_array_t items;
+	LLFindWearablesEx is_worn(/*is_worn=*/ true, /*include_body_parts=*/ false);
+	gInventory.collectDescendentsIf(outfit_cat_id,
+		cats,
+		items,
+		LLInventoryModel::EXCLUDE_TRASH,
+		is_worn);
+	return items.size() > 0;
+}
+
+// static
+bool LLAppearanceMgr::getCanAddToCOF(const LLUUID& outfit_cat_id)
+{
+	LLInventoryModel::cat_array_t cats;
+	LLInventoryModel::item_array_t items;
+	LLFindWearablesEx not_worn(/*is_worn=*/ false, /*include_body_parts=*/ false);
+	gInventory.collectDescendentsIf(outfit_cat_id,
+		cats,
+		items,
+		LLInventoryModel::EXCLUDE_TRASH,
+		not_worn);
+	return items.size() > 0;
+}
+
 void LLAppearanceMgr::purgeBaseOutfitLink(const LLUUID& category)
 {
 	LLInventoryModel::cat_array_t cats;
@@ -1338,9 +1366,12 @@ void LLAppearanceMgr::updateCOF(const LLUUID& category, bool append)
 
 	// - Body parts: always include COF contents as a fallback in case any
 	// required parts are missing.
+	// Preserve body parts from COF if appending.
 	LLInventoryModel::item_array_t body_items;
 	getDescendentsOfAssetType(cof, body_items, LLAssetType::AT_BODYPART, false);
 	getDescendentsOfAssetType(category, body_items, LLAssetType::AT_BODYPART, false);
+	if (append)
+		reverse(body_items.begin(), body_items.end());
 	// Reduce body items to max of one per type.
 	removeDuplicateItems(body_items);
 	filterWearableItems(body_items, 1);
diff --git a/indra/newview/llappearancemgr.h b/indra/newview/llappearancemgr.h
index e42f9f7d6f4..8ded32a53d4 100644
--- a/indra/newview/llappearancemgr.h
+++ b/indra/newview/llappearancemgr.h
@@ -75,6 +75,12 @@ class LLAppearanceMgr: public LLSingleton<LLAppearanceMgr>
 	// Determine whether a given outfit can be removed.
 	bool getCanRemoveOutfit(const LLUUID& outfit_cat_id);
 
+	// Determine whether we're wearing any of the outfit contents (excluding body parts).
+	static bool getCanRemoveFromCOF(const LLUUID& outfit_cat_id);
+
+	// Determine whether we can add anything (but body parts) from the outfit contents to COF.
+	static bool getCanAddToCOF(const LLUUID& outfit_cat_id);
+
 	// Copy all items in a category.
 	void shallowCopyCategoryContents(const LLUUID& src_id, const LLUUID& dst_id,
 									 LLPointer<LLInventoryCallback> cb);
diff --git a/indra/newview/llinventorybridge.cpp b/indra/newview/llinventorybridge.cpp
index ec367c1746f..1881d0d780e 100644
--- a/indra/newview/llinventorybridge.cpp
+++ b/indra/newview/llinventorybridge.cpp
@@ -2489,7 +2489,7 @@ void LLFolderBridge::folderOptionsMenu()
 			mItems.push_back(std::string("Wear As Ensemble"));
 		}
 		mItems.push_back(std::string("Remove From Outfit"));
-		if (!areAnyContentsWorn(model))
+		if (!LLAppearanceMgr::getCanRemoveFromCOF(mUUID))
 		{
 			disabled_items.push_back(std::string("Remove From Outfit"));
 		}
@@ -2514,19 +2514,6 @@ BOOL LLFolderBridge::checkFolderForContentsOfType(LLInventoryModel* model, LLInv
 	return ((item_array.count() > 0) ? TRUE : FALSE );
 }
 
-BOOL LLFolderBridge::areAnyContentsWorn(LLInventoryModel* model) const
-{
-	LLInventoryModel::cat_array_t cat_array;
-	LLInventoryModel::item_array_t item_array;
-	LLFindWorn is_worn;
-	model->collectDescendentsIf(mUUID,
-								cat_array,
-								item_array,
-								LLInventoryModel::EXCLUDE_TRASH,
-								is_worn);
-	return (item_array.size() > 0);
-}
-
 // Flags unused
 void LLFolderBridge::buildContextMenu(LLMenuGL& menu, U32 flags)
 {
diff --git a/indra/newview/llinventorybridge.h b/indra/newview/llinventorybridge.h
index 757808eb93c..64d0f8d2545 100644
--- a/indra/newview/llinventorybridge.h
+++ b/indra/newview/llinventorybridge.h
@@ -295,7 +295,6 @@ class LLFolderBridge : public LLInvFVBridge
 	static void createNewEyes(void* user_data);
 
 	BOOL checkFolderForContentsOfType(LLInventoryModel* model, LLInventoryCollectFunctor& typeToCheck);
-	BOOL areAnyContentsWorn(LLInventoryModel* model) const;
 
 	void modifyOutfit(BOOL append);
 	void determineFolderType();
diff --git a/indra/newview/llinventoryfunctions.cpp b/indra/newview/llinventoryfunctions.cpp
index de24bd92d0c..469d1888dde 100644
--- a/indra/newview/llinventoryfunctions.cpp
+++ b/indra/newview/llinventoryfunctions.cpp
@@ -573,6 +573,31 @@ bool LLFindWearables::operator()(LLInventoryCategory* cat,
 	return FALSE;
 }
 
+LLFindWearablesEx::LLFindWearablesEx(bool is_worn, bool include_body_parts)
+:	mIsWorn(is_worn)
+,	mIncludeBodyParts(include_body_parts)
+{}
+
+bool LLFindWearablesEx::operator()(LLInventoryCategory* cat, LLInventoryItem* item)
+{
+	LLViewerInventoryItem *vitem = dynamic_cast<LLViewerInventoryItem*>(item);
+	if (!vitem) return false;
+
+	// Skip non-wearables.
+	if (!vitem->isWearableType() && vitem->getType() != LLAssetType::AT_OBJECT)
+	{
+		return false;
+	}
+
+	// Skip body parts if requested.
+	if (!mIncludeBodyParts && vitem->getType() == LLAssetType::AT_BODYPART)
+	{
+		return false;
+	}
+
+	return (bool) get_is_item_worn(item->getUUID()) == mIsWorn;
+}
+
 bool LLFindWearablesOfType::operator()(LLInventoryCategory* cat, LLInventoryItem* item)
 {
 	if (!item) return false;
@@ -593,11 +618,6 @@ void LLFindWearablesOfType::setType(LLWearableType::EType type)
 	mWearableType = type;
 }
 
-bool LLFindWorn::operator()(LLInventoryCategory* cat, LLInventoryItem* item)
-{
-	return item && get_is_item_worn(item->getUUID());
-}
-
 bool LLFindNonRemovableObjects::operator()(LLInventoryCategory* cat, LLInventoryItem* item)
 {
 	if (item)
diff --git a/indra/newview/llinventoryfunctions.h b/indra/newview/llinventoryfunctions.h
index 93c56e1b8a6..1795b546797 100644
--- a/indra/newview/llinventoryfunctions.h
+++ b/indra/newview/llinventoryfunctions.h
@@ -337,6 +337,21 @@ class LLFindWearables : public LLInventoryCollectFunctor
 							LLInventoryItem* item);
 };
 
+//~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
+// Class LLFindWearablesEx
+//
+// Collects wearables based on given criteria.
+//~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
+class LLFindWearablesEx : public LLInventoryCollectFunctor
+{
+public:
+	LLFindWearablesEx(bool is_worn, bool include_body_parts = true);
+	virtual bool operator()(LLInventoryCategory* cat, LLInventoryItem* item);
+private:
+	bool mIncludeBodyParts;
+	bool mIsWorn;
+};
+
 //Inventory collect functor collecting wearables of a specific wearable type
 class LLFindWearablesOfType : public LLInventoryCollectFunctor
 {
@@ -363,15 +378,6 @@ class LLFindActualWearablesOfType : public LLFindWearablesOfType
 	}
 };
 
-// Find worn items.
-class LLFindWorn : public LLInventoryCollectFunctor
-{
-public:
-	LLFindWorn() {}
-	virtual ~LLFindWorn() {}
-	virtual bool operator()(LLInventoryCategory* cat, LLInventoryItem* item);
-};
-
 // Collect non-removable folders and items.
 class LLFindNonRemovableObjects : public LLInventoryCollectFunctor
 {
diff --git a/indra/newview/lloutfitslist.cpp b/indra/newview/lloutfitslist.cpp
index 8c6189353e1..73c850e1fb2 100644
--- a/indra/newview/lloutfitslist.cpp
+++ b/indra/newview/lloutfitslist.cpp
@@ -102,6 +102,14 @@ class OutfitContextMenu : public LLListContextMenu
 		{
 			return get_is_category_renameable(&gInventory, outfit_cat_id);
 		}
+		else if ("wear_add" == param)
+		{
+			return LLAppearanceMgr::getCanAddToCOF(outfit_cat_id);
+		}
+		else if ("take_off" == param)
+		{
+			return LLAppearanceMgr::getCanRemoveFromCOF(outfit_cat_id);
+		}
 
 		return true;
 	}
@@ -119,14 +127,6 @@ class OutfitContextMenu : public LLListContextMenu
 		{
 			return !is_worn;
 		}
-		else if ("wear_add" == param)
-		{
-			return !is_worn;
-		}
-		else if ("take_off" == param)
-		{
-			return is_worn;
-		}
 		else if ("delete" == param)
 		{
 			return LLAppearanceMgr::instance().getCanRemoveOutfit(outfit_cat_id);
diff --git a/indra/newview/llpaneloutfitsinventory.cpp b/indra/newview/llpaneloutfitsinventory.cpp
index e2563efb7d5..2405b95e7dd 100644
--- a/indra/newview/llpaneloutfitsinventory.cpp
+++ b/indra/newview/llpaneloutfitsinventory.cpp
@@ -191,6 +191,10 @@ class LLOutfitListGearMenu
 		{
 			return LLAppearanceMgr::instance().getCanRemoveOutfit(selected_outfit_id);
 		}
+		else if ("take_off" == param)
+		{
+			return LLAppearanceMgr::getCanRemoveFromCOF(selected_outfit_id);
+		}
 
 		return true;
 	}
@@ -209,10 +213,6 @@ class LLOutfitListGearMenu
 		{
 			return !is_worn;
 		}
-		else if ("take_off" == param)
-		{
-			return is_worn;
-		}
 
 		return true;
 	}
diff --git a/indra/newview/skins/default/xui/en/menu_outfit_tab.xml b/indra/newview/skins/default/xui/en/menu_outfit_tab.xml
index e084216a69d..9206969b2f2 100644
--- a/indra/newview/skins/default/xui/en/menu_outfit_tab.xml
+++ b/indra/newview/skins/default/xui/en/menu_outfit_tab.xml
@@ -18,6 +18,9 @@
      name="wear_add">
         <on_click
          function="Outfit.WearAdd" />
+        <on_enable
+         function="Outfit.OnEnable"
+         parameter="wear_add" />
         <on_visible
          function="Outfit.OnVisible"
          parameter="wear_add" />
@@ -28,6 +31,9 @@
      name="take_off">
         <on_click
          function="Outfit.TakeOff" />
+        <on_enable
+         function="Outfit.OnEnable"
+         parameter="take_off" />
         <on_visible
          function="Outfit.OnVisible"
          parameter="take_off" />
-- 
GitLab