summaryrefslogtreecommitdiff
path: root/indra/newview/llcofwearables.cpp
diff options
context:
space:
mode:
Diffstat (limited to 'indra/newview/llcofwearables.cpp')
-rw-r--r--indra/newview/llcofwearables.cpp217
1 files changed, 198 insertions, 19 deletions
diff --git a/indra/newview/llcofwearables.cpp b/indra/newview/llcofwearables.cpp
index 1925b818f2..ee366f4e3c 100644
--- a/indra/newview/llcofwearables.cpp
+++ b/indra/newview/llcofwearables.cpp
@@ -35,10 +35,16 @@
#include "llcofwearables.h"
#include "llagentdata.h"
+#include "llagentwearables.h"
#include "llappearancemgr.h"
#include "llinventory.h"
#include "llinventoryfunctions.h"
+#include "lllistcontextmenu.h"
+#include "llmenugl.h"
+#include "llviewermenu.h"
#include "llwearableitemslist.h"
+#include "llpaneloutfitedit.h"
+#include "llsidetray.h"
static LLRegisterPanelClassWrapper<LLCOFAccordionListAdaptor> t_cof_accodion_list_adaptor("accordion_list_adaptor");
@@ -46,6 +52,117 @@ static LLRegisterPanelClassWrapper<LLCOFWearables> t_cof_wearables("cof_wearable
const LLSD REARRANGE = LLSD().with("rearrange", LLSD());
+static const LLWearableItemNameComparator WEARABLE_NAME_COMPARATOR;
+
+
+//////////////////////////////////////////////////////////////////////////
+
+class CofAttachmentContextMenu : public LLListContextMenu
+{
+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");
+ }
+};
+
+//////////////////////////////////////////////////////////////////////////
+
+class CofClothingContextMenu : public LLListContextMenu
+{
+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);
+ }
+
+};
+
+//////////////////////////////////////////////////////////////////////////
+
+class CofBodyPartContextMenu : public LLListContextMenu
+{
+protected:
+
+ /*virtual*/ LLContextMenu* createMenu()
+ {
+ LLUICtrl::CommitCallbackRegistry::ScopedRegistrar registrar;
+ LLUICtrl::EnableCallbackRegistry::ScopedRegistrar enable_registrar;
+ LLUUID selected_id = mUUIDs.back();
+
+ // *HACK* need to pass pointer to LLPanelOutfitEdit instead of LLSideTray::getInstance()->getPanel().
+ // LLSideTray::getInstance()->getPanel() is rather slow variant
+ LLPanelOutfitEdit* panel_oe = dynamic_cast<LLPanelOutfitEdit*>(LLSideTray::getInstance()->getPanel("panel_outfit_edit"));
+ registrar.add("BodyPart.Replace", boost::bind(&LLPanelOutfitEdit::onReplaceBodyPartMenuItemClicked, panel_oe, selected_id));
+ 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;
+ }
+};
+
+//////////////////////////////////////////////////////////////////////////
LLCOFWearables::LLCOFWearables() : LLPanel(),
mAttachments(NULL),
@@ -53,8 +170,17 @@ LLCOFWearables::LLCOFWearables() : LLPanel(),
mBodyParts(NULL),
mLastSelectedList(NULL)
{
+ mClothingMenu = new CofClothingContextMenu();
+ mAttachmentMenu = new CofAttachmentContextMenu();
+ mBodyPartMenu = new CofBodyPartContextMenu();
};
+LLCOFWearables::~LLCOFWearables()
+{
+ delete mClothingMenu;
+ delete mAttachmentMenu;
+ delete mBodyPartMenu;
+}
// virtual
BOOL LLCOFWearables::postBuild()
@@ -63,6 +189,9 @@ BOOL LLCOFWearables::postBuild()
mClothing = getChild<LLFlatListView>("list_clothing");
mBodyParts = getChild<LLFlatListView>("list_body_parts");
+ mClothing->setRightMouseDownCallback(boost::bind(&LLCOFWearables::onListRightClick, this, _1, _2, _3, mClothingMenu));
+ mAttachments->setRightMouseDownCallback(boost::bind(&LLCOFWearables::onListRightClick, this, _1, _2, _3, mAttachmentMenu));
+ mBodyParts->setRightMouseDownCallback(boost::bind(&LLCOFWearables::onListRightClick, this, _1, _2, _3, mBodyPartMenu));
//selection across different list/tabs is not supported
mAttachments->setCommitCallback(boost::bind(&LLCOFWearables::onSelectionChange, this, mAttachments));
@@ -73,6 +202,10 @@ BOOL LLCOFWearables::postBuild()
mClothing->setCommitOnSelectionChange(true);
mBodyParts->setCommitOnSelectionChange(true);
+ //clothing is sorted according to its position relatively to the body
+ mAttachments->setComparator(&WEARABLE_NAME_COMPARATOR);
+ mBodyParts->setComparator(&WEARABLE_NAME_COMPARATOR);
+
return LLPanel::postBuild();
}
@@ -114,7 +247,7 @@ void LLCOFWearables::refresh()
populateAttachmentsAndBodypartsLists(cof_items);
- LLAppearanceMgr::wearables_by_type_t clothing_by_type(WT_COUNT);
+ LLAppearanceMgr::wearables_by_type_t clothing_by_type(LLWearableType::WT_COUNT);
LLAppearanceMgr::getInstance()->divvyWearablesByType(cof_items, clothing_by_type);
populateClothingList(clothing_by_type);
@@ -150,7 +283,7 @@ void LLCOFWearables::populateAttachmentsAndBodypartsLists(const LLInventoryModel
LLPanelInventoryListItemBase* item_panel = NULL;
if (item_type == LLAssetType::AT_OBJECT)
{
- item_panel = LLPanelInventoryListItemBase::create(item);
+ item_panel = buildAttachemntListItem(item);
mAttachments->addItem(item_panel, item->getUUID(), ADD_BOTTOM, false);
}
else if (item_type == LLAssetType::AT_BODYPART)
@@ -164,29 +297,30 @@ void LLCOFWearables::populateAttachmentsAndBodypartsLists(const LLInventoryModel
if (mAttachments->size())
{
- mAttachments->sort(); //*TODO by Name
+ mAttachments->sort();
mAttachments->notify(REARRANGE); //notifying the parent about the list's size change (cause items were added with rearrange=false)
}
if (mBodyParts->size())
{
- mBodyParts->sort(); //*TODO by name
+ mBodyParts->sort();
+ mBodyParts->notify(REARRANGE);
}
-
- mBodyParts->notify(REARRANGE);
}
//create a clothing list item, update verbs and show/hide line separator
LLPanelClothingListItem* LLCOFWearables::buildClothingListItem(LLViewerInventoryItem* item, bool first, bool last)
{
llassert(item);
-
+ if (!item) return NULL;
LLPanelClothingListItem* item_panel = LLPanelClothingListItem::create(item);
if (!item_panel) return NULL;
//updating verbs
//we don't need to use permissions of a link but of an actual/linked item
if (item->getLinkedItem()) item = item->getLinkedItem();
+ llassert(item);
+ if (!item) return NULL;
bool allow_modify = item->getPermissions().allowModifyBy(gAgentID);
@@ -199,8 +333,8 @@ LLPanelClothingListItem* LLCOFWearables::buildClothingListItem(LLViewerInventory
//setting callbacks
//*TODO move that item panel's inner structure disclosing stuff into the panels
item_panel->childSetAction("btn_delete", mCOFCallbacks.mDeleteWearable);
- item_panel->childSetAction("btn_move_up", mCOFCallbacks.mMoveWearableCloser);
- item_panel->childSetAction("btn_move_down", mCOFCallbacks.mMoveWearableFurther);
+ item_panel->childSetAction("btn_move_up", mCOFCallbacks.mMoveWearableFurther);
+ item_panel->childSetAction("btn_move_down", mCOFCallbacks.mMoveWearableCloser);
item_panel->childSetAction("btn_edit", mCOFCallbacks.mEditWearable);
//turning on gray separator line for the last item in the items group of the same wearable type
@@ -212,14 +346,15 @@ LLPanelClothingListItem* LLCOFWearables::buildClothingListItem(LLViewerInventory
LLPanelBodyPartsListItem* LLCOFWearables::buildBodypartListItem(LLViewerInventoryItem* item)
{
llassert(item);
-
+ if (!item) return NULL;
LLPanelBodyPartsListItem* item_panel = LLPanelBodyPartsListItem::create(item);
if (!item_panel) return NULL;
//updating verbs
//we don't need to use permissions of a link but of an actual/linked item
if (item->getLinkedItem()) item = item->getLinkedItem();
-
+ llassert(item);
+ if (!item) return NULL;
bool allow_modify = item->getPermissions().allowModifyBy(gAgentID);
item_panel->setShowLockButton(!allow_modify);
item_panel->setShowEditButton(allow_modify);
@@ -232,22 +367,38 @@ LLPanelBodyPartsListItem* LLCOFWearables::buildBodypartListItem(LLViewerInventor
return item_panel;
}
+LLPanelDeletableWearableListItem* LLCOFWearables::buildAttachemntListItem(LLViewerInventoryItem* item)
+{
+ llassert(item);
+ if (!item) return NULL;
+
+ LLPanelDeletableWearableListItem* item_panel = LLPanelDeletableWearableListItem::create(item);
+ if (!item_panel) return NULL;
+
+ //setting callbacks
+ //*TODO move that item panel's inner structure disclosing stuff into the panels
+ item_panel->childSetAction("btn_delete", mCOFCallbacks.mDeleteWearable);
+
+ return item_panel;
+}
+
void LLCOFWearables::populateClothingList(LLAppearanceMgr::wearables_by_type_t& clothing_by_type)
{
- llassert(clothing_by_type.size() == WT_COUNT);
+ llassert(clothing_by_type.size() == LLWearableType::WT_COUNT);
- for (U32 type = WT_SHIRT; type < WT_COUNT; ++type)
+ for (U32 type = LLWearableType::WT_SHIRT; type < LLWearableType::WT_COUNT; ++type)
{
U32 size = clothing_by_type[type].size();
if (!size) continue;
LLAppearanceMgr::sortItemsByActualDescription(clothing_by_type[type]);
- for (U32 i = 0; i < size; i++)
+ //clothing items are displayed in reverse order, from furthest ones to closest ones (relatively to the body)
+ for (U32 i = size; i != 0; --i)
{
- LLViewerInventoryItem* item = clothing_by_type[type][i];
+ LLViewerInventoryItem* item = clothing_by_type[type][i-1];
- LLPanelClothingListItem* item_panel = buildClothingListItem(item, i == 0, i == size - 1);
+ LLPanelClothingListItem* item_panel = buildClothingListItem(item, i == size, i == 1);
if (!item_panel) continue;
mClothing->addItem(item_panel, item->getUUID(), ADD_BOTTOM, false);
@@ -262,16 +413,17 @@ void LLCOFWearables::populateClothingList(LLAppearanceMgr::wearables_by_type_t&
//adding dummy items for missing wearable types
void LLCOFWearables::addClothingTypesDummies(const LLAppearanceMgr::wearables_by_type_t& clothing_by_type)
{
- llassert(clothing_by_type.size() == WT_COUNT);
+ llassert(clothing_by_type.size() == LLWearableType::WT_COUNT);
- for (U32 type = WT_SHIRT; type < WT_COUNT; type++)
+ for (U32 type = LLWearableType::WT_SHIRT; type < LLWearableType::WT_COUNT; type++)
{
U32 size = clothing_by_type[type].size();
if (size) continue;
- EWearableType w_type = static_cast<EWearableType>(type);
+ LLWearableType::EType w_type = static_cast<LLWearableType::EType>(type);
LLPanelInventoryListItemBase* item_panel = LLPanelDummyClothingListItem::create(w_type);
if(!item_panel) continue;
+ item_panel->childSetAction("btn_add", mCOFCallbacks.mAddWearable);
mClothing->addItem(item_panel, LLUUID::null, ADD_BOTTOM, false);
}
}
@@ -283,6 +435,21 @@ LLUUID LLCOFWearables::getSelectedUUID()
return mLastSelectedList->getSelectedUUID();
}
+bool LLCOFWearables::getSelectedUUIDs(uuid_vec_t& selected_ids)
+{
+ if (!mLastSelectedList) return false;
+
+ mLastSelectedList->getSelectedUUIDs(selected_ids);
+ return selected_ids.size() != 0;
+}
+
+LLPanel* LLCOFWearables::getSelectedItem()
+{
+ if (!mLastSelectedList) return NULL;
+
+ return mLastSelectedList->getSelectedItem();
+}
+
void LLCOFWearables::clear()
{
mAttachments->clear();
@@ -290,4 +457,16 @@ void LLCOFWearables::clear()
mBodyParts->clear();
}
+void LLCOFWearables::onListRightClick(LLUICtrl* ctrl, S32 x, S32 y, LLListContextMenu* menu)
+{
+ if(menu)
+ {
+ uuid_vec_t selected_uuids;
+ if(getSelectedUUIDs(selected_uuids))
+ {
+ menu->show(ctrl, selected_uuids, x, y);
+ }
+ }
+}
+
//EOF