summaryrefslogtreecommitdiff
path: root/indra
diff options
context:
space:
mode:
Diffstat (limited to 'indra')
-rw-r--r--indra/llui/llview.cpp2
-rw-r--r--indra/newview/llagentwearables.cpp62
-rw-r--r--indra/newview/llagentwearables.h14
-rw-r--r--indra/newview/llappearancemgr.cpp88
-rw-r--r--indra/newview/llappearancemgr.h6
-rw-r--r--indra/newview/llcofwearables.cpp69
-rw-r--r--indra/newview/llinventorybridge.cpp15
-rw-r--r--indra/newview/llinventoryfunctions.cpp29
-rw-r--r--indra/newview/llinventoryfunctions.h14
-rw-r--r--indra/newview/llinventorymodel.cpp5
-rw-r--r--indra/newview/llinventorymodel.h1
-rw-r--r--indra/newview/lloutfitslist.cpp15
-rw-r--r--indra/newview/llpanelobjectinventory.cpp6
-rw-r--r--indra/newview/llwearableitemslist.cpp116
-rw-r--r--indra/newview/llwearableitemslist.h9
-rw-r--r--indra/newview/skins/default/xui/en/panel_outfit_edit.xml1
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"