From 953e0a619abba8d85faf8bf3e59d59e344bfc566 Mon Sep 17 00:00:00 2001
From: Vadim Savchuk <vsavchuk@productengine.com>
Date: Mon, 9 Aug 2010 18:10:36 +0300
Subject: EXT-8577 FIXED Context menu items for multi-attachments

Changes:
* Implemented bulk-add from My Appearance SP.
* Made sure there's no memleak when you click Wear/Attach in the in-world object context menu
  and the callback isn't invoked (because e.g. avatar fails to get close enough to the object).
  I stated that in comments.

Reviewed by Seraph at https://codereview.productengine.com/secondlife/r/844/

--HG--
branch : product-engine
---
 indra/newview/llviewermenu.cpp        |  3 +-
 indra/newview/llvoavatar.cpp          |  9 ++++++
 indra/newview/llvoavatar.h            |  1 +
 indra/newview/llwearableitemslist.cpp | 56 +++++++++++++++++++++++++++--------
 indra/newview/llwearableitemslist.h   |  2 +-
 5 files changed, 56 insertions(+), 15 deletions(-)

diff --git a/indra/newview/llviewermenu.cpp b/indra/newview/llviewermenu.cpp
index 92195f0a4d..a289a0eb7a 100644
--- a/indra/newview/llviewermenu.cpp
+++ b/indra/newview/llviewermenu.cpp
@@ -5950,7 +5950,8 @@ void LLObjectAttachToAvatar::confirmReplaceAttachment(S32 option, LLViewerJointA
 			delta = delta * 0.5f;
 			walkToSpot -= delta;
 
-			CallbackData* user_data = new CallbackData(attachment_point, mReplace); // *TODO: leak if the callback isn't called?
+			// The callback will be called even if avatar fails to get close enough to the object, so we won't get a memory leak.
+			CallbackData* user_data = new CallbackData(attachment_point, mReplace);
 			gAgent.startAutoPilotGlobal(gAgent.getPosGlobalFromAgent(walkToSpot), "Attach", NULL, onNearAttachObject, user_data, stop_distance);
 			gAgentCamera.clearFocusObject();
 		}
diff --git a/indra/newview/llvoavatar.cpp b/indra/newview/llvoavatar.cpp
index 298ac28ca8..6392aad248 100644
--- a/indra/newview/llvoavatar.cpp
+++ b/indra/newview/llvoavatar.cpp
@@ -5641,6 +5641,15 @@ BOOL LLVOAvatar::canAttachMoreObjects() const
 	return (getNumAttachments() < MAX_AGENT_ATTACHMENTS);
 }
 
+//-----------------------------------------------------------------------------
+// canAttachMoreObjects()
+// Returns true if we can attach <n> more objects.
+//-----------------------------------------------------------------------------
+BOOL LLVOAvatar::canAttachMoreObjects(U32 n) const
+{
+	return (getNumAttachments() + n) <= MAX_AGENT_ATTACHMENTS;
+}
+
 //-----------------------------------------------------------------------------
 // lazyAttach()
 //-----------------------------------------------------------------------------
diff --git a/indra/newview/llvoavatar.h b/indra/newview/llvoavatar.h
index 5de08e8e27..3f603dda8b 100644
--- a/indra/newview/llvoavatar.h
+++ b/indra/newview/llvoavatar.h
@@ -687,6 +687,7 @@ public:
 	void 				rebuildHUD();
 	void 				resetHUDAttachments();
 	BOOL				canAttachMoreObjects() const;
+	BOOL				canAttachMoreObjects(U32 n) const;
 protected:
 	U32					getNumAttachments() const; // O(N), not O(1)
 
diff --git a/indra/newview/llwearableitemslist.cpp b/indra/newview/llwearableitemslist.cpp
index 194213f880..e2a5489fcf 100644
--- a/indra/newview/llwearableitemslist.cpp
+++ b/indra/newview/llwearableitemslist.cpp
@@ -869,7 +869,7 @@ void LLWearableItemsList::ContextMenu::updateItemsVisibility(LLContextMenu* menu
 	setMenuItemVisible(menu, "wear_wear", 			n_already_worn == 0 && n_worn == 0 && can_be_worn);
 	setMenuItemEnabled(menu, "wear_wear", 			n_already_worn == 0 && n_worn == 0);
 	setMenuItemVisible(menu, "wear_add",			wear_add_visible);
-	setMenuItemEnabled(menu, "wear_add",			n_items == 1 && canAddWearable(ids.front()));
+	setMenuItemEnabled(menu, "wear_add",			canAddWearables(ids));
 	setMenuItemVisible(menu, "wear_replace",		n_worn == 0 && n_already_worn != 0 && can_be_worn);
 	//visible only when one item selected and this item is worn
 	setMenuItemVisible(menu, "edit",				!standalone && mask & (MASK_CLOTHING|MASK_BODYPART) && n_worn == n_items && n_worn == 1);
@@ -978,31 +978,61 @@ void LLWearableItemsList::ContextMenu::createNewWearable(const LLUUID& item_id)
 	LLAgentWearables::createWearable(item->getWearableType(), true);
 }
 
-// Can we wear another wearable of the given item's wearable type?
+// Returns true if all the given objects and clothes can be added.
 // static
-bool LLWearableItemsList::ContextMenu::canAddWearable(const LLUUID& item_id)
+bool LLWearableItemsList::ContextMenu::canAddWearables(const uuid_vec_t& item_ids)
 {
 	// TODO: investigate wearables may not be loaded at this point EXT-8231
 
-	LLViewerInventoryItem* item = gInventory.getItem(item_id);
-	if (!item)
+	U32 n_objects = 0;
+	boost::unordered_map<LLWearableType::EType, U32> clothes_by_type;
+
+	// Count given clothes (by wearable type) and objects.
+	for (uuid_vec_t::const_iterator it = item_ids.begin(); it != item_ids.end(); ++it)
 	{
-		return false;
+		LLViewerInventoryItem* item = gInventory.getItem(*it);
+		if (!item)
+		{
+			return false;
+		}
+
+		if (item->getType() == LLAssetType::AT_OBJECT)
+		{
+			++n_objects;
+		}
+		else if (item->getType() == LLAssetType::AT_CLOTHING)
+		{
+			++clothes_by_type[item->getWearableType()];
+		}
+		else
+		{
+			llwarns << "Unexpected wearable type" << llendl;
+			return false;
+		}
 	}
 
-	if (item->getType() == LLAssetType::AT_OBJECT)
+	// Check whether we can add all the objects.
+	if (!isAgentAvatarValid() || !gAgentAvatarp->canAttachMoreObjects(n_objects))
 	{
-		// *TODO: is this the right check?
-		return isAgentAvatarValid() && gAgentAvatarp->canAttachMoreObjects();
+		return false;
 	}
 
-	if (item->getType() != LLAssetType::AT_CLOTHING)
+	// Check whether we can add all the clothes.
+	boost::unordered_map<LLWearableType::EType, U32>::const_iterator m_it;
+	for (m_it = clothes_by_type.begin(); m_it != clothes_by_type.end(); ++m_it)
 	{
-		return false;
+		LLWearableType::EType w_type	= m_it->first;
+		U32 n_clothes					= m_it->second;
+
+		U32 wearable_count = gAgentWearables.getWearableCount(w_type);
+		if ((wearable_count + n_clothes) > LLAgentWearables::MAX_CLOTHING_PER_TYPE)
+		{
+			return false;
+		}
+
 	}
 
-	U32 wearable_count = gAgentWearables.getWearableCount(item->getWearableType());
-	return wearable_count < LLAgentWearables::MAX_CLOTHING_PER_TYPE;
+	return true;
 }
 
 // EOF
diff --git a/indra/newview/llwearableitemslist.h b/indra/newview/llwearableitemslist.h
index 81f1cd1b40..d7970e0838 100644
--- a/indra/newview/llwearableitemslist.h
+++ b/indra/newview/llwearableitemslist.h
@@ -431,7 +431,7 @@ public:
 		static void setMenuItemEnabled(LLContextMenu* menu, const std::string& name, bool val);
 		static void updateMask(U32& mask, LLAssetType::EType at);
 		static void createNewWearable(const LLUUID& item_id);
-		static bool canAddWearable(const LLUUID& item_id);
+		static bool canAddWearables(const uuid_vec_t& item_ids);
 
 		LLWearableItemsList*	mParent;
 	};
-- 
cgit v1.2.3