diff options
author | Vadim Savchuk <vsavchuk@productengine.com> | 2010-05-25 14:17:11 +0300 |
---|---|---|
committer | Vadim Savchuk <vsavchuk@productengine.com> | 2010-05-25 14:17:11 +0300 |
commit | cb0589715265dc1568626fe238aac7417b44ef89 (patch) | |
tree | b7f5e613796711088b1a99a2f80ede461fa181fb | |
parent | d634239bac4ee94d96a17b4ba68015c9f90b727a (diff) |
EXT-6726 WIP Added handlers for most of Appearance SP context/gear menus.
Reviewed by Mike Antipov and Nyx at https://codereview.productengine.com/secondlife/r/428/
--HG--
branch : product-engine
-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" |