diff options
| -rw-r--r-- | indra/llui/llview.cpp | 2 | ||||
| -rw-r--r-- | indra/newview/llagentwearables.cpp | 62 | ||||
| -rw-r--r-- | indra/newview/llagentwearables.h | 14 | ||||
| -rw-r--r-- | indra/newview/llappearancemgr.cpp | 88 | ||||
| -rw-r--r-- | indra/newview/llappearancemgr.h | 6 | ||||
| -rw-r--r-- | indra/newview/llcofwearables.cpp | 69 | ||||
| -rw-r--r-- | indra/newview/llinventorybridge.cpp | 15 | ||||
| -rw-r--r-- | indra/newview/llinventoryfunctions.cpp | 29 | ||||
| -rw-r--r-- | indra/newview/llinventoryfunctions.h | 14 | ||||
| -rw-r--r-- | indra/newview/llinventorymodel.cpp | 5 | ||||
| -rw-r--r-- | indra/newview/llinventorymodel.h | 1 | ||||
| -rw-r--r-- | indra/newview/lloutfitslist.cpp | 15 | ||||
| -rw-r--r-- | indra/newview/llpanelobjectinventory.cpp | 6 | ||||
| -rw-r--r-- | indra/newview/llwearableitemslist.cpp | 116 | ||||
| -rw-r--r-- | indra/newview/llwearableitemslist.h | 9 | ||||
| -rw-r--r-- | indra/newview/skins/default/xui/en/panel_outfit_edit.xml | 1 | 
16 files changed, 397 insertions, 55 deletions
diff --git a/indra/llui/llview.cpp b/indra/llui/llview.cpp index c091686ffb..bd56da9121 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 6b5e43973e..68c4fa1ea0 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 734bd9fd47..1f19d1045b 100644 --- a/indra/newview/llagentwearables.h +++ b/indra/newview/llagentwearables.h @@ -71,17 +71,18 @@ protected:  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 @@ public:  	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; @@ -134,6 +136,14 @@ protected:  	void			recoverMissingWearableDone();  	//-------------------------------------------------------------------- +	// Editing/moving wearables +	//-------------------------------------------------------------------- + +public: +	static void		editWearable(const LLUUID& item_id); +	bool			moveWearable(const LLViewerInventoryItem* item, bool closer_to_body); + +	//--------------------------------------------------------------------  	// Removing wearables  	//--------------------------------------------------------------------  public: diff --git a/indra/newview/llappearancemgr.cpp b/indra/newview/llappearancemgr.cpp index dcef86a5fc..2eb7cfd34a 100644 --- a/indra/newview/llappearancemgr.cpp +++ b/indra/newview/llappearancemgr.cpp @@ -152,27 +152,6 @@ public:  }; -//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 516dada39d..96541beb7d 100644 --- a/indra/newview/llappearancemgr.h +++ b/indra/newview/llappearancemgr.h @@ -58,6 +58,9 @@ public:  	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 @@ public:  	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 @@ public:  	// 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 47862ad921..dfc203111a 100644 --- a/indra/newview/llcofwearables.cpp +++ b/indra/newview/llcofwearables.cpp @@ -61,6 +61,11 @@ protected:  	/*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 @@ protected:  	/*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 @@ protected:  	/*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 2d27c89074..35f7cbcd01 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 2b4d9fb25c..c38d45f0f5 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 79b9b4a9cc..8b96ba29d9 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 @@ public:  							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 c373512ace..23df6b6cc5 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 1f7bd50328..7b56d0bdd1 100644 --- a/indra/newview/llinventorymodel.h +++ b/indra/newview/llinventorymodel.h @@ -258,6 +258,7 @@ public:  	// 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 36832c9d16..17a2db7a43 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 39ade40967..0d3beaa9a5 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 5836252eac..fb7577c008 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 d7b09ca934..7ad1b5a3ad 100644 --- a/indra/newview/llwearableitemslist.h +++ b/indra/newview/llwearableitemslist.h @@ -287,7 +287,16 @@ public:  	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 b473a7a282..895cc4e3cc 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"  | 
